Related
I want to write a (Prolog) DCG which takes in strings of the form a2rev(a) where a is a string of binary bits, e.g. 1012101, 001121100, 0111002001110. My idea was the following :
reverse([]) --> [].
reverse([H|T]) --> reverse(T), [H].
s--> [2].
s--> a,2,b.
a--> [0];[1].
reverse(a,b).
This doesn't work - I am unsure whether I am calling the reverse function incorrectly or if a --> [0];[1] makes sense.
Any help appreciated
You are not mixing the dcg and code properly. Here b//1 extracts a binary sequence and s//1 extracts a binary sequence which has your required property.
b([]) --> [].
b([0 | X]) --> [0], b(X).
b([1 | X]) --> [1], b(X).
s(X) --> b(X), [2], {reverse(X, Y)}, b(Y).
The reverse constraint is added in a {} block using the reverse/2 predicate. If instead you want to use reverse//1 dcg itself you can replace s//1 above with:
s(X) --> b(X), [2], reverse(X).
This gives
?- phrase(s(X), [1, 1, 0, 2, 0, 1, 1]).
X = [1, 1, 0]
For better clarity of what is going on when mixing code and dcg try checking listing(b) and listing(s).
The following looks very unusual :
?- findall(X, member(X, [1, 2, 3]), X).
X = [1, 2, 3].
The trace even more so
?- trace, findall(X, member(X, [1, 2, 3]), X).
^ Call: (11) findall(_100058, member(_100058, [1, 2, 3]), _100058) ? creep
^ Exit: (11) findall([1, 2, 3], user:member([1, 2, 3], [1, 2, 3]), [1, 2, 3]) ? creep
X = [1, 2, 3]
Thinking in terms of semantics of findall this makes little sense. What is going on?
To expand on my comments, maybe this might help:
?- findall(X, member(X, [1, 2, 3]), Xs).
Xs = [1, 2, 3].
If you look closely, you will see that Prolog (SWI, in this case) did not print a substitution for X. This means that X is not bound when the query succeeds. Indeed:
?- findall(X, member(X, [1, 2, 3]), Xs), var(X).
Xs = [1, 2, 3].
This does not mean that X is never bound while the query executes:
?- findall(X, ( member(X, [1, 2, 3]), writeln(X) ), Xs), var(X).
1
2
3
Xs = [1, 2, 3].
But after all solutions have been generated, X is unbound and can be bound to some other value -- such as the list of solutions. This will work in any standard conforming Prolog, as the standard says explicitly that findall only tries to unify its third argument after it has created the list of solutions. It even contains an example with sharing between the template and the list of instantiations:
findall(X, (X=1;X=2), [X, Y]).
Succeeds, unifying X with 1, and Y with 2.
So how does this binding and unbinding work? With a failure-driven loop, as quoted in rajashekar's answer from the SWI-Prolog implementation. In general, succeeding predicates bind some variables. When at some later point something fails (or, equivalently, the user presses ; when prompted by the toplevel), backtracking takes place: It unbinds variables to allow them to take new values, then retries some goal.
What goes on inside findall is the same as goes on when you write the following:
?- ( member(X, [1, 2, 3]), writeln(X), false ; true ), var(X).
1
2
3
true.
So while findall is very impure, it is not so impure as to be completely un-Prolog-like. In fact, we can write our own:
:- dynamic my_findall_bag/1.
my_findall(Template, Goal, Instances) :-
% initialization
retractall(my_findall_bag(_)),
asserta(my_findall_bag([])),
% collect solutions
( call(Goal),
copy_term(Template, NewSolution),
retract(my_findall_bag(PreviousSolutions)),
asserta(my_findall_bag([NewSolution | PreviousSolutions])),
% failure-driven loop: after saving the solution, force Goal to
% generate a new one
false
; true ),
% cleanup and finish; the saved solutions are in reversed order (newest
% first), so reverse them
retract(my_findall_bag(AllSavedSolutions)),
reverse(AllSavedSolutions, Instances).
This behaves as expected:
?- my_findall(X, member(X, [1, 2, 3]), Xs).
Xs = [1, 2, 3].
Or even:
?- my_findall(X, member(X, [1, 2, 3]), X).
X = [1, 2, 3].
A minor problem with this is that the instantiation of Goal should be checked. A major problem with this is that all my_findall calls share the same bag, so calling my_findall from inside a my_findall (or in parallel) will make you unhappy. This could be fixed using some sort of gensym mechanism to give each my_findall run its unique key into the database.
As for the trace output, it is an unfortunate consequence of wanting to express "your goal succeeded with such-and-such bindings" on one line. At the point of success, it is true that findall(X, ..., X) succeeded, and it is true that X = [1, 2, 3], and hence it is true that the successful instance of the goal is findall([1, 2, 3], ..., [1, 2, 3]).
Consider:
forty_two(FortyTwo) :-
var(FortyTwo),
FortyTwo = 42.
my_call(Goal) :-
format('about to call ~w~n', [Goal]),
call(Goal),
format('success: ~w~n', [Goal]).
For example:
?- my_call(forty_two(X)).
about to call forty_two(_2320)
success: forty_two(42)
X = 42.
So forty_two(42) is a succeeding instance of forty_two(X). Even though forty_two(42) does not succeed:
?- forty_two(42).
false.
It is logical that printing the term forty_two(X) in an environment with X = 42 prints forty_two(42). I think the problem is that this logical behavior sticks out as strange among all the non-logical stuff going on here.
I did some code diving to try and figure out what is going on. In swi-prolog listing(findall, [source(true)]). gives the following code :
findall(Templ, Goal, List) :-
findall(Templ, Goal, List, []).
findall(Templ, Goal, List, Tail) :-
setup_call_cleanup(
'$new_findall_bag',
findall_loop(Templ, Goal, List, Tail),
'$destroy_findall_bag').
findall_loop in the appropriate file is as follows :
findall_loop(Templ, Goal, List, Tail) :-
( Goal,
'$add_findall_bag'(Templ) % fails
; '$collect_findall_bag'(List, Tail)
).
After consulting the C source files, I found out that findall/4 is setting up a global variable in C-source ('$new_findall_bag') and findall_loop/4 is pushing the Templ to it when the Goal succeeds (with '$add_findall_bag'(Templ)). When the Goal fails Templ is uninstantiated and hence the final clause '$collect_findall_bag'(List, Tail) succeeds even when List and Templ are the same variable.
We can see in trace that Templ is usuall uninstantiated.
?- trace, findall(X, member(X, [1, 2, 3]), Xs).
^ Call: (11) findall(_28906, member(_28906, [1, 2, 3]), _28916) ? creep
^ Exit: (11) findall(_28906, user:member(_28906, [1, 2, 3]), [1, 2, 3]) ? creep
Xs = [1, 2, 3].
So the process of finding all instantiations of Templ so that the Goal succeeds is separate from the process of collecting all those instantiations into the variable List and hence we can use the same variable without causing and error. But the semantics of writing such a clause is not making much sense to me.
EDIT: Similar situation occurs in gprolog, where the process of collecting solutions and that of retriving them are separate. Relevant Yap code also looks quite similar, but i was not able to install it to check.
I would like to ask, if anyone knows how to improve (if it's not optimal) this code.
The idea, is that you have a list of elements, and I want to return a list, with two sublists inside it, the first sublist should contain the elements that are contained in the odd positions of the list, and the second sublist should contain, the elements that are contained in the even positions of the list.
Some examples:
?-evenAndOdd([1,2,3,4,5],[[1,3,5],[2,4]])
True.
?-evenAndOdd([a,b,c,d,e],[[a,c,e],[b,d]]).
True.
The code I have implemented is the next one:
evenAndOdd([],[]).
evenAndOdd([H|R],NL):-
evenAndOddRec([H|R], [[],[]],1,NL).
evenAndOddRec([], [LOdd,LEven],_,[LOdd,LEven]).
evenAndOddRec([H|R],[LOdd,LEven],Pos,NL):-
\+ even(Pos),
!,
NPos is Pos +1,
append(LOdd,[H],NLOdd),
evenAndOddRec(R,[NLOdd,LEven],NPos,NL).
evenAndOddRec([H|R],[LOdd,LEven],Pos,NL):-
NPos is Pos + 1,
append(LEven, [H], NLEven),
evenAndOddRec(R,[LOdd, NLEven],NPos,NL).
even(N):-
N mod 2 =:=0.
One symptom that the code is not optimal is that it will run off into the woods if you ask for an additional solution in the -,+,+ instantiation pattern:
?- evenAndOdd(X, [[1,3,5], [2,4,6]]).
X = [1, 2, 3, 4, 5, 6] ;
<time passes>
This kind of thing is a frequent occurrence when manually trying to match up lists with indexes in Prolog.
Stylistically, I would rather not give back a list containing exactly two lists when I could just have three arguments instead of two; this is, after all, a relationship between three lists, the combined list and the even and odd items.
Additionally, just eyeballing it, I'm not sure why any arithmetic or any cuts are needed here. This is how I would implement it:
evenAndOdd([], [], []).
evenAndOdd([O], [O], []).
evenAndOdd([O,E|Rest], [O|ORest], [E|ERest]) :- evenAndOdd(Rest, ORest, ERest).
This works with many instantiations:
?- evenAndOdd([1,2,3,4,5,6], O, E).
O = [1, 3, 5],
E = [2, 4, 6].
?- evenAndOdd([1,2,3,4,5], O, E).
O = [1, 3, 5],
E = [2, 4] ;
false.
?- evenAndOdd(X, [1,3,5], [2,4]).
X = [1, 2, 3, 4, 5] ;
false.
?- evenAndOdd(X, [1,3,5], [2,4,6]).
X = [1, 2, 3, 4, 5, 6].
?- evenAndOdd(X, [1,3,5], [2,4,6,8]).
false.
?- evenAndOdd([1,2,3,4,5,6], X, [2,4,6,8]).
false.
?- evenAndOdd([1,2,3,4,5,6], X, [2,4,6]).
X = [1, 3, 5].
You can implicitly determine even and odd values upon recursion, by taking two elements at a time (and taking into account when the has an odd number of elements):
evenAndOdd(L, [LOdd, LEven]):-
evenAndOdd(L, LOdd, LEven).
evenAndOdd([], [], []).
evenAndOdd([Odd], [Odd], []).
evenAndOdd([Odd,Even|Tail], [Odd|LOdd], [Even|LEven]):-
evenAndOdd(Tail, LOdd, LEven).
A good language for logic programming should allow the programmer to use a language close to the language used by the mathematicians. Therefore, I have always considered the lack of proper universal quantifier in Prolog an important shortcoming.
Today an idea came to me how to define something much better than forall and foreach.
forany(Var, {Context}, Condition, Body)
This predicate tries to prove Body for all instantiations Var gets successively on backtracking over Condition. All variables in Condition and Body are considered local unless listed in Var or Context. Condition is not permitted to modify in any way the variables listed in Context, otherwise forany won't work correctly.
Here is the implementation (based on yall):
forany(V, {Vars}, Goal1, Goal2) :-
( bagof(V, {V,Vars}/Goal1, Solutions)
-> maplist({Vars}/[V]>>Goal2, Solutions)
; true ).
My first question is about the second argument of forany. I'd like to eliminate it.
Now some examples
Construct a list of the first 8 squares:
?- length(X,8), forany(N, {X}, between(1,8,N),
(Q is N*N, nth1(N, X, Q))).
X = [1, 4, 9, 16, 25, 36, 49, 64].
Reverse a list:
?- X=[1,2,3,4,5], length(X,N), length(Y,N),
forany(I, {X,Y,N}, between(1,N,I),
(J is N-I+1, nth1(I,X,A), nth1(J,Y,A))).
X = [1, 2, 3, 4, 5],
N = 5,
Y = [5, 4, 3, 2, 1].
Subset:
subset(X, Y) :- forany(A, {X,Y}, member(A,X), member(A, Y)).
A funny way to generate all permutations of a list without duplicates:
permutation(X, Y) :-
length(X, N), length(Y, N), subset(X, Y).
?- permutation([1,2,3],X).
X = [1, 2, 3] ;
X = [1, 3, 2] ;
X = [2, 1, 3] ;
X = [2, 3, 1] ;
X = [3, 1, 2] ;
X = [3, 2, 1] ;
false.
A funny way to sort a list of different integers. Notice that constraints are used to make the list sorted so most permutations won't be generated:
sorted(X) :- forany(A-B, {X}, append(_, [A,B|_], X),
A#<B).
?- X=[7,3,8,2,6,4,9,5,1], length(X, N), length(Y, N),
sorted(Y), subset(X,Y).
X = [7, 3, 8, 2, 6, 4, 9, 5, 1],
N = 9,
Y = [1, 2, 3, 4, 5, 6, 7, 8, 9] .
The problem
It seems that this forany works brilliantly when constraints are not used. Also, it can be used to generate constraints, but at least on SWI-Prolog there are problems when constraints already have been generated. The reason for this is that forany uses bagof and according to the manual of SWI-Prolog:
Term-copying operations (assertz/1, retract/1, findall/3, copy_term/2, etc.) generally also copy constraints. The effect varies from ok, silent copying of huge constraint networks to violations of the internal consistency of constraint networks. As a rule of thumb, copying terms holding attributes must be deprecated. If you need to reason about a term that is involved in constraints, use copy_term/3 to obtain the constraints as Prolog goals, and use these goals for further processing.
Here is a demonstration of the problem bagof creates with constraints:
?- X=[A,B,C], dif(C,D), bagof(_, K^member(K,X), _).
X = [A, B, C],
dif(C, _5306),
dif(C, _5318),
dif(C, _5330),
dif(C, D).
As you can see, three unnecessary constraints are created.
My second question is if this is a problem only of SWI-Prolog.
And the third question: is there a way to fix this in SWI-Prolog. The above quote from the manual suggests that copy_term/3 should be used. Unfortunately, I don't understand this suggestion and I don't know if it is useful for forany.
Great news! I was surprised that bagof is written in Prolog. By looking at its code I learned that some things I thought are impossible are in fact possible. And just as the manual of SWI-Prolog suggested, copy_term/3 or rather the similar predicate copy_term_nat/2 helped.
So with great joy I am able to present a fully working (as far as I can tell) universal quantifier for SWI-Prolog:
forany(V, {Vars}, Condition, Body) :-
findall(V-Vars, Condition, Solutions),
% For SWI-Prolog. Can be replaced by Solutions=Clean_solutions in other systems
copy_term_nat(Solutions, Clean_solutions),
forany_execute_goals(Clean_solutions, Vars, V, Body).
forany_execute_goals([], _, _, _).
forany_execute_goals([Sol-NewVars|Solutions], Vars, V, Body) :-
% The following test can be removed
assertion(subsumes_term(NewVars, Vars)),
% or replaced by the following more standard use of throw/1:
% ( subsumes_term(NewVars, Vars)
% -> true
% ; throw('Forbidden instantiation of context variables by the antecedent of forany') ),
NewVars = Vars,
call({Vars}/[V]>>Body, Sol),
forany_execute_goals(Solutions, Vars, V, Body).
I have a homework assignment using prolog and I'm translating a set of 1's and 0's to a different set of characters. For example, here are my facts
analog([1], .).
analog([1, 1, 1], -).
analog([0], "").
analog([0, 0, 0], ^).
analog([0, 0, 0, 0, 0, 0, 0], #).
An example input would be a list like
[1,1,1,0,1,1,1,
0,0,0,1,1,1,0,1,1,1,0,1,1,1,0,0,0,1,0,1,1,1,
0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0,
1,1,1,0,1,0,1,1,1,0,1,0,0,0,1,1,1,0,1,1,1,
0,1,1,1]
I have the following code :
signal_morse([], []). %base case
signal_morse([A, B, C | Rest, R) :-
analog([A, B, C], H),
signal_morse(Rest, T),
append([H], T, R).
It only currently checks the first three elements in a list and I want to be able to check for 1 element if the first three don't match with any of the facts.
For example, say I have [1, 0, 0], since that doesn't match with any of my rules I want the program to check [1] instead and keep looking through the rest of the list.
So I wanted to know if there is any sort of pattern matching I can do so if the code analog([A, B, C], H) fails to find a match, the code would then try to match just the first character like analog([A], H).
I realize this probably doesn't help you with your homework, but this is such a perfect problem for DCGs I can't help but show you what it would look like.
analog(.) --> [1].
analog(-) --> [1,1,1].
analog("") --> [0].
analog(^) --> [0,0,0].
analog(#) --> [0,0,0,0,0,0,0].
analogs([]) --> [].
analogs([A|As]) --> analog(A), analogs(As).
Usage:
?- phrase(analogs(X), [1,1,1,0,1,1,1, 0,0,0,1,1,1,0,1,1,1,0,1,1,1,0,0,0,1,0,1,1,1, 0,1,0,0,0,1,0,1,0,1,0,0,0,1,0,0,0,0,0,0,0, 1,1,1,0,1,0,1,1,1,0,1,0,0,0,1,1,1,0,1,1,1, 0,1,1,1]).
X = ['.', '.', '.', "", '.', '.', '.', "", ""|...] ;
Anyway, to answer your actual question, Prolog can figure out the lengths on its own with something like this:
signal_morse([], []).
signal_morse(Signals, [Code|RemainingMorseCodes]) :-
append(Signal, RemainingSignals, Signals),
analog(Signal, Code),
signal_morse(RemainingSignals, RemainingMorseCodes).