Hie, I am a newbie in Prolog, especially recursion in Prolog. Here, start recursive on list X, makepairs recursive on list Y. These two rules should make a list of the pairs of items on X and Y. For example, if I enter query:
?- start([a,b], [c,d], Z)
Prolog should print out:
Z = [pair(a,c), pair(a,d), pair(b,c), pair(b,d)].
But my code prints only false
. Could anyone help to find the bug in my code?
start([H|T], Y, Z):- makepairs(H, Y, Z), start(T, Y, Z).
start([], _Y, []).
makepairs(X, [H|T], Z) :- append([pair(X,H)], [], Z), makepairs(X, T, Z).
makepairs(_X, [], _Z).
CodePudding user response:
Using library(lamdba)
preinstalled in Scryer, available for
SICStus|SWI.
start(Xs, Ys, XYs) :-
maplist(\X^Y^pair(X,Y)^true, Xs, Ys, XYs).
Or manually,
list_list_pairs([], [], []).
list_list_pairs([X|Xs], [Y|Ys], [pair(X,Y)|Pairs]) :-
list_list_pairs(Xs, Ys, Pairs).
Note that this not only constructs such pairs, but it can also be used to "unzip" them:
?- list_list_pairs(Xs, Ys, [pair(1,one), pair(2,two), pair(3,three)]).
Xs = [1,2,3], Ys = [one,two,three].
But, maybe I should add, using pair/2
as a functor is fairly uncommon. It is more idiomatic in Prolog to use (-)/2
instead, which is so much more compact, think of
Ps = [1-one,2-two,3-three].
% vs
Ps = [pair(1,one), pair(2,two), pair(3,three)]
CodePudding user response:
Assuming that this is a learning exercise, you are supposed to roll your own...
For the purposes of this exercise, I'm going to elect to represent the tuple using the :/2
operator (x:y
), simply because it's less typing, more compact, and and easier on the eyes than, say, [x,y]
.
So, there are 3 cases in this problem:
Both lists are the empty list. The result is the empty list. You can represent that as
zip( [] , [] , [] ) .
Both lists are non-empty lists. The result is that we prepend the pair to the result list, and continue, thus:
zip( [X|Xs] , [Y|Ys] , [X:Y|Zs] ) :- zip(Xs,Ys,Zs) .
And then, there is the case where one list is empty and the other is non-empty. You need to consider how to handle that situation. You could
- Fail.
- Terminate the program and succeed, discarding the unmatched items.
- Include the unpaired item as itself.
- Include the unpaired item in the same
x:y
pair structure, selecting some atom to represent the missing data.
Here, I choose to include the last option, and will represent the missing data with the atom
nil
, so[nil:y]
or[x:nil]
.You can represent this in Prolog as
zip( [] , [Y|Ys] , [nil:Y|Zs] ) :- zip([],Ys,Zs) . zip( [X|Xs] , [] , [X:nil|Zs] ) :- zip(Xs,[],Zs) .
Putting it all together, you get this:
zip( [] , [] , [] ) .
zip( [] , [Y|Ys] , [nil:Y|Zs] ) :- zip([],Ys,Zs) .
zip( [X|Xs] , [] , [X:nil|Zs] ) :- zip(Xs,[],Zs) .
zip( [X|Xs] , [Y|Ys] , [X:Y|Zs] ) :- zip(Xs,Ys,Zs) .
which you can fiddle with at https://swish.swi-prolog.org/p/zip/unzip.pl
Running
zip( [a,b,c], [1,2,3], Ps ).
results in
P = [ a:1, b:2, c:3 ]
You might note that this will also unzip a list of tuples, so running
zip(Xs,Ys,[a:1,b:2,c:3]).
results in
Xs = [a,b,c]
Ys = [1,2,3]
CodePudding user response:
This line cannot succeed:
start([H|T],Y,Z):- makepairs(H,Y,Z), start(T,Y,Z).
The first part requires Prolog to find a value for Z
which is the list of all pairs, the second part requires Z
to become only the list of H paired with every element in Y, and the third part requires Z
to become the pairs of the tail and Y.
One single Z
cannot be three different things at the same time, the predicate cannot hold, and false
must be the result.
This line:
start([],_Y,[]).
Introduces a helper variable, which you never use.
This line:
makepairs(X,[H|T],Z):-append([pair(X,H)],[], Z), makepairs(X,T,Z).
requires that Z
is the list of pairs of X
with everything in [H|T]
, and also that Z
is what you get when you append an empty list onto a list with one pair, and that Z
is the list of pairs of X
with T
. This has the same problem of requiring Z
to be three different things. These lines are where you need to introduce helper variables such as Z_
.
CodePudding user response:
Flaws in the predicate makepairs/3
- In the base case,
makepairs(_X, [], _Z)
, the variable_Z
remains free, but it should be instantiated to[]
. - In the recursive case,
append([pair(X, H), [], Z]
is equivalent toZ = [pair(X, H)]
(however, all you need isZ = pair(X,H)
); also, the recursive call must produce a list containing the remaining pairs, sayZs
. So the final result should be[Z|Zs]
.
Thus, a correct code is:
makepairs(_X, [], []).
makepairs(X, [H|T], [Z|Zs]):-
Z = pair(X,H),
makepairs(X, T, Zs).
Example:
?- makepairs(a, [c,d], Pairs).
Pairs = [pair(a, c), pair(a, d)] ;
false. % <= spurious choice point!
Flaws in predicate start/3
- In the recursive case, subgoal
makepairs(H, Y, Z)
produces a listZ
containing only pairs whose first elements are equal toH
; then, the recursive call must produce a listZs
containing the remaining pairs. So, the final result, sayZss
, must be the concatenation of listsZ
andZs
.
Thus, a correct code is:
start([], _Y, []).
start([H|T], Y, Zss):-
makepairs(H, Y, Z),
append(Z, Zs, Zss),
start(T, Y, Zs).
Example:
?- start([a,b], [c,d], Z).
Z = [pair(a, c), pair(a, d), pair(b, c), pair(b, d)] ;
false. % <= spurious choice point
A BETTER SOLUTION
A better solution, that avoid spurious choice point using first argument indexing, is:
make_pairs([], _, []).
make_pairs([X|Xs], Y, Pairs):-
make_pairs_helper(Y, X, Px),
append(Px, Pxs, Pairs),
make_pairs(Xs, Y, Pxs).
make_pairs_helper([], _, []).
make_pairs_helper([Y|Ys], X, [pair(X,Y)|Zs]):-
make_pairs_helper(Ys, X, Zs).
Example:
?- make_pairs([a,b], [c,d], Pairs).
Pairs = [pair(a, c), pair(a, d), pair(b, c), pair(b, d)].
Remark It's more idiomatic to use -
as separator of the elements of a pair in Prolog.