The following Prolog program defines a predicate rev/2 for reversing a list passed in first argument which results in the list passed in second argument:
rev([], []).
rev([XH|XT], Y) :-
rev(XT, Z),
append(Z, [XH], Y).
append([], Y, Y).
append([XH|XT], Y, [XH|ZT]) :-
append(XT, Y, ZT).
The following Prolog program is an alternative implementation of rev/2:
rev(X, Y) :-
revappend(X, [], Y).
revappend([], Y, Y).
revappend([XH|XT], Y, Z) :-
revappend(XT, [XH|Y], Z).
Both programs work as expected for queries in this argument mode:
?- rev([a, b, c], Y).
Y = [c, b, a]
; false.
But both programs exhaust resources for queries in this argument mode:
?- rev(X, [a, b, c]).
X = [c, b, a]
;
Time limit exceeded
Questions:
How to fix both programs?
Are both programs equivalent?
In both programs, the third argument has no influence on (universal) termination, as can be seen by the following failure slices:
rev([], []) :- false.
rev([XH|XT], Y) :-
rev(XT, Z), false,
append(Z, [XH], Y).
Y can be whatever it wants, it will never cause failure in this fragment. Thus it has no influence on termination. It is termination neutral.
rev(X, Y) :-
revappend(X, [], Y), false.
revappend([], Y, Y) :- false.
revappend([XH|XT], Y, Z) :-
revappend(XT, [XH|Y], Z), false.
Similarly, the third argument in revappend/3 is just handed over without any chance of causing failure and thus termination.
In order to fix the problem something has to be added to specialize the remaining visible part. One observation is that the list length of both the first and the last argument is the same. And thus adding an extra fourth argument for ensuring that both arguments are of same length will help to get the optimal termination condition:
rev(X, Y) :-
revappend(X, [], Y, Y).
revappend([], Y, Y, []).
revappend([XH|XT], Y, Z, [_|Ylen]) :-
revappend(XT, [XH|Y], Z, Ylen).
And here is a generalization of this program to better understand how termination is influenced by the arguments:
rev(X, Y) :-
revappend(X, _, _, Y).
revappend([], _, _, []).
revappend([_|XT], _, _, [_|Ylen]) :-
revappend(XT, _, _, Ylen).
So the 2nd and 3rd argument is just replaced by _. This generalization is now exactly same_length/2.
Related
I am new to prolog and still learning the language.
I can't understand why my code in Prolog is not working.
I am trying to make some kind of translator with lists.
Input: translate([strings to translate], [dictionary], [translation])
ex. translate([m,n], [(m,c), (m,d), (n,e), (c,f)], X)
Needed output: X=[c,d,e]
Actual output: False
contain(,[]).
contain(X,[X|]).
contain(X,[_|L]) :- contain(X,L).
contain_2(X, [(X, Y) | ], Y).
contain_2(X, [|T], Y) :-
contain_2(X, T, Y).
tr(XS, L, Y) :-
contain(Z, XS),
contain_2(Z, L, Y).
translate(M, N, R) :-
setof(Y, tr(M, N, Y), R).
My code no matter what returns False, but I can't find the reason. I tried debugging and it seems like it returns false before entering setof. I have used setof before, but maybe I'm missing something. I changed the structure different times, but this version seems to return the least amount of warnings.
Several lines look like they are missing _.
I think the main issue is that contain_2 has no base case for when the list of pairs is empty. It will get to contain_2(X, [], Y) find no predicate matching that and fail. e.g.
?- trace, contain_2([1,2], [(1,a),(2,b)], [a,b]).
Call:contain_2([1, 2],[(1,a), (2,b)],[a, b])
Call:contain_2([1, 2],[(2,b)],[a, b])
Call:contain_2([1, 2],[],[a, b])
Fail:contain_2([1, 2],[],[a, b])
You need:
contain_2(_, [], _).
contain_2(X, [(X, Y) | _], Y).
contain_2(X, [_|T], Y) :-
contain_2(X, T, Y).
I want remove all appearences of an element on a list, similar to this, but in my case, the list may have non-instantiated variables. For example:
delMember(z, [A,B,A,z], L).
L = [A, B, A];
false.
and
delMember(A, [A, B, A, z], L).
L = [B,z];
false.
I tried defining delMember as the following:
delMember(_, [], []).
delMember(X, [X|Xs], Y) :- delMember(X, Xs, Y).
delMember(X, [T|Xs], [T|Y]) :- X \== T, delMember(X, Xs, Y).
With this definition, the last result I get is correct but it's still trying to instantiate the variables before that.
?- delMember(A, [A,B,A,z], R).
A = B, B = z,
R = [] ;
A = B,
R = [z] ;
A = z,
R = [B] ;
R = [B, z] ;
any ideas???
If you look at your second predicate clause:
delMember(X, [X|Xs], Y) :- delMember(X, Xs, Y).
Unification is occurring with the X in the first and second arguments. This leads to the results you are observing when you do your query. You need to apply the same operator as you did in your third clause. So your complete predicate (with some slightly changed variable names to be more conventional) would look like:
delMember(_, [], []).
delMember(X, [X1|Xs], Ys) :- X == X1, delMember(X, Xs, Ys).
delMember(X, [X1|Xs], [X1|Ys]) :- X \== X1, delMember(X, Xs, Ys).
I have a Prolog problem here, I am trying to get unique airports into the list but my predicate does not work as expected.
not_member(C, []).
not_member(C, [H|L]) :-
not_member(C, L),
C \== H.
path(X, Y, [X,Y]) :-
flight(X, Y, _, _, _, _).
path(X, Y, [X,P]) :-
not_member(Z, P),
flight(X, Z, _, _, _, _),
flight(Z, Y, _, _, _, _),
path(Z, Y, P).
Sample query with expected answers:
?- path(dublin, rome, L)
L = [dublin, rome] ;
L = [dublin, paris, rome] ...
If you need facts let me know, your help will be appreciated. Thanks!
The problem is not the (\==)/2. The problem is that an uninstantiated P would make not_member/2 loop. So you need a predicate path/4 with four arguments:
:- use_module(library(basic/lists)).
path(_, X, L, _) :- member(X, L), !, fail.
path(X, X, L, [X|L]).
path(Y, X, L, R) :-
flight(Z, X),
path(Y, Z, [X|L], R).
The above predicate searches from the destination airport backwards, so that we don't need to reverse the resulting list. Here is an example database:
flight(zurich, frankfurt).
flight(frankfurt, zurich).
flight(zurich, munich).
flight(munich, zurich).
flight(munich, frankfurt).
flight(frankfurt, munich).
And here is an example run:
Jekejeke Prolog 2, Runtime Library 1.2.5
(c) 1985-2017, XLOG Technologies GmbH, Switzerland
?- path(zurich, frankfurt, [], L).
L = [zurich,frankfurt] ;
L = [zurich,munich,frankfurt] ;
No
I am given a list, an element to be replaced and the replacement for that element. I managed to do it for all occurrences of the element in that list:
replace([],X,Y,[]).
replace([X|T], X, Y, Z) :-
replace(T, X, Y, Z1),
Z = [Y|Z1].
replace([H|T], X, Y, [H|Z]) :-
replace(T, X, Y, Z).
However now I must only replace the first occurrence of that element.
My thought process writing:
replace([X|T], X, Y, Z) :-
replace(T, X, Y, Z1),
Z = [Y|Z1].
was:
Z is the result of [X|T],X,Y if Z1 is the result of T,XY and Z = [Y|Z1]. Following the same thought process I tried writing the function that only replaces the first occurrence of the element like so:
An idea for implementing only the first occurrence would be from a replace/4 to go in a replace/5 where I count if I replaced or not like so:
replace_single(L,X,Y,Z) :-
replace2(L,X,Y,0,Z).
replace2([],X,Y,C,[]).
replace2([X|T], X, Y, C, Z) :-
\+ (C = 0),
replace2(T, X, Y, C, Z1),
Z = [X|Z1],
C is 1.
replace2([H|T], X, Y, C, [H|Z]) :-
replace2(T, X, Y, C, Z).
Obviously it will not work, I am a little bit lost. Could someone give me a tip or so of how I could think to solve the problem or the solution itself?
We define replace/4 based upon same_length/2, append/3, maplist/2, and prolog-dif:
replace(Xs,X,Y,Ys) :-
same_length(Xs,Ys),
append(Prefix,[X|Suffix],Xs),
maplist(dif(X),Prefix),
append(Prefix,[Y|Suffix],Ys).
Sample queries:
?- replace(Xs,2,two,[1,two,3,4,5,1,2,3,4,5,1,2]).
Xs = [1,2,3,4,5,1,2,3,4,5,1,2]
; false.
?- replace([1,2,3,4,5,1,2,3,4,5,1,2],2,two,Ys).
Ys = [1,two,3,4,5,1,2,3,4,5,1,2]
; false.
Another approach would be to use a DCG:
rep1(X, Y, [Z|T]) --> [Z], { dif(Z, X) }, rep1(X, Y, T).
rep1(X, Y, [Y|T]) --> [X], rest(T).
rep1(_, _, []) --> [].
rest([]) --> [].
rest([H|T]) --> [H], rest(T).
| ?- phrase(rep1(a, 1, L), [a,b,c,a,d]).
L = [1,b,c,a,d] ? ;
| ?- phrase(rep1(a, 1, [x, y, 1, b]), L).
L = [x,y,1,b] ? a
L = [x,y,a,b]
You can write your predicate as:
replace(X, Y, L, R) :- phrase(rep1(X, Y, R), L).
Another way to do it will be:-
replace(E1,L1,E2,L2) :-
same_length(L1,L2),
append(BeforeElement,[E1|AfterElement],L1),
append(BeforeElement,[E2|AfterElement],L2).
where BeforeElement stands for prefix of the list before the element and After Element stands for suffix of the list after the element.
How can I replace a list with another list that contain the variable to be replaced. for example
rep([x, d, e, z, x, z, p], [x=z, z=x, d=c], R).
R = [z, c, e, x, z, x, p]
the x to z and z doesn't change after it has been replaced.
so far I did only the one without the list
rep([], _, []).
rep(L1, H1=H2, L2) :-
rep(L1, H1, H2, L2).
rep([],_,_,[]).
rep([H|T], X1, X2, [X2|L]) :-
H=X1,
rep(T,X1,X2,L),
!.
rep([H|T],X1,X2,[H|L]) :-
rep(T,X1,X2,L).
If you use SWI-Prolog, with module lambda.pl found there : http://www.complang.tuwien.ac.at/ulrich/Prolog-inedit/lambda.pl you can write :
:- use_module(library(lambda)).
rep(L, Rep, New_L) :-
maplist(\X^Y^(member(X=Z, Rep)
-> Y = Z
; Y = X), L, New_L).
You should attempt to keep the code simpler than possible:
rep([], _, []).
rep([X|Xs], Vs, [Y|Ys]) :-
( memberchk(X=V, Vs) -> Y = V ; Y = X ),
rep(Xs, Vs, Ys).
Of course, note the idiomatic way (thru memberchk/2) to check for a variable value.
Still yet a more idiomatic way to do: transforming lists it's a basic building block in several languages, and Prolog is no exception:
rep(Xs, Vs, Ys) :- maplist(repv(Vs), Xs, Ys).
repv(Vs, X, Y) :- memberchk(X=V, Vs) -> Y = V ; Y = X .
Here's how you could proceed using if_/3 and (=)/3.
First, we try to find a single Key in a list of pairs K-V.
An extra argument reifies search success.
pairs_key_firstvalue_t([] ,_ ,_ ,false).
pairs_key_firstvalue_t([K-V|KVs],Key,Value,Truth) :-
if_(K=Key,
(V=Value, Truth=true),
pairs_key_firstvalue_t(KVs,Key,Value,Truth)).
Next, we need to handle "not found" cases:
assoc_key_mapped(Assoc,Key,Value) :-
if_(pairs_key_firstvalue_t(Assoc,Key,Value),
true,
Key=Value).
Last, we put it all together using the meta-predicate maplist/3:
?- maplist(assoc_key_mapped([x-z,z-x,d-c]), [x,d,e,z,a,z,p], Rs).
Rs = [z,c,e,x,a,x,p]. % OK, succeeds deterministically
Let's improve this answer by moving the "recursive part" into meta-predicate find_first_in_t/4:
:- meta_predicate find_first_in_t(2,?,?,?).
find_first_in_t(P_2,X,Xs,Truth) :-
list_first_suchthat_t(Xs,X,P_2,Truth).
list_first_suchthat_t([] ,_, _ ,false).
list_first_suchthat_t([E|Es],X,P_2,Truth) :-
if_(call(P_2,E),
(E=X,Truth=true),
list_first_suchthat_t(Es,X,P_2,Truth)).
To fill in the "missing bits and pieces", we define key_pair_t/3:
key_pair_t(Key,K-_,Truth) :-
=(Key,K,Truth).
Based on find_first_in_t/4 and key_pair_t/3, we can write assoc_key_mapped/3 like this:
assoc_key_mapped(Assoc,Key,Value) :-
if_(find_first_in_t(key_pair_t(Key),_-Value,Assoc),
true,
Key=Value).
So, does the OP's use-case still work?
?- maplist(assoc_key_mapped([x-z,z-x,d-c]), [x,d,e,z,a,z,p], Rs).
Rs = [z,c,e,x,a,x,p]. % OK. same result as before
Building on find_first_in_t/4
memberd_t(X,Xs,Truth) :- % memberd_t/3
find_first_in_t(=(X),_,Xs,Truth).
:- meta_predicate exists_in_t(2,?,?). % exists_in_t/3
exists_in_t(P_2,Xs,Truth) :-
find_first_in_t(P_2,_,Xs,Truth).
I find your code rather confused. For one thing, you have rep/3 and rep/4, but none of them have a list in the second position where you're passing the list of variable bindings. H1=H2 cannot possibly match a list, and that's the only rep/3 clause that examines the second argument. If this is a class assignment, it looks like you're a little bit behind and I'd suggest you spend some time on the previous material.
The solution is simpler than you'd think:
rep([], _, []).
rep([X|Xs], Vars, [Y|Rest]) :- member(X=Y, Vars), rep(Xs, Vars, Rest).
rep([X|Xs], Vars, [X|Rest]) :- \+ member(X=_, Vars), rep(Xs, Vars, Rest).
We're using member/2 to find a "variable binding" in the list (in quotes because these are atoms and not true Prolog variables). If it's in the list, Y is the replacement, otherwise we keep using X. And you see this has the desired effect:
?- rep([x, d, e, z, x, z, p], [x=z, z=x, d=c], R).
R = [z, c, e, x, z, x, p] ;
false.
This could be made somewhat more efficient using "or" directly (and save us a choice point):
rep([], _, []).
rep([X|Xs], Vars, [Y|Ys]) :-
(member(X=Y, Vars), ! ; X=Y),
rep(Xs, Vars, Ys).
See:
?- rep([x, d, e, z, x, z, p], [x=z, z=x, d=c], R).
R = [z, c, e, x, z, x, p].