Reputation: 101
For the following query (and the predicates defined in the following) I get an unexpected answer:
?- rep([1,2,3], 3, [2,3,4], L).
L = [1, 2, 2, 3, 4] ;
L = [1, 2, 3]. % unexpected answer
The first result is the one I want. The second one I do not want...
How can I prevent the second one? Probably by adding !
somewhere?
concat([], L, L).
concat([H|T], L, [H|Res]) :-
concat(T, L, Res).
repl([], _, _, []).
repl([Val|T], Val, Repl, Res) :-
repl(T, Val, Repl, Temp),
concat(Repl, Temp, Res).
repl([H|T], Val, Repl, [H|Res]) :-
repl(T, Val, Repl, Res).
Upvotes: 1
Views: 1879
Reputation: 60014
edit
this is getting hairy, and my answer didn't accounted precisely for request... so let's see your code with minimal change:
concat([], L, L).
concat([H|T], L, [H|Res]) :-
concat(T, L, Res).
repl([], _, _, []).
repl([Val|T], Val, Repl, Res) :- !, % as noted by @repeat, better to commit early...
repl(T, Val, Repl, Temp),
concat(Repl, Temp, Res). % !.
repl([H|T], Val, Repl, [H|Res]) :-
repl(T, Val, Repl, Res).
the cut simply commits the second clause...
resume old answer
your concat/3 is the same as the well known append/3, so consider this approach
repl(ListOrig, Element, Replace, ListUpdated) :-
append(H, [Element|T], ListOrig),
append(H, Replace, Temp),
append(Temp, T, ListUpdated).
?- repl([1, 2, 3], 3, [2, 3, 4], L).
L = [1, 2, 2, 3, 4] ;
false.
edit
as requested by comments, this extension handles a list of Element to match for change, with simple pattern matching (note: add before the previous clause)
repl(ListOrig, [], _Replace, ListOrig).
repl(ListOrig, [E|Es], Replace, ListUpdated) :-
repl(ListOrig, E, Replace, Temp),
repl(Temp, Es, Replace, ListUpdated).
test
?- repl([1,2,3],[2,3],[x,y,z],R).
R = [1, x, y, z, x, y, z] ;
false.
edit
I didn't noticed that if Element is not found it should not fail... a last 'catchall' clause could handle this case:
repl(ListOrig, _Element, _Replace, ListOrig).
or better, extend the original like
repl(ListOrig, Element, Replace, ListUpdated) :-
( append(H, [Element|T], ListOrig)
-> append(H, Replace, Temp),
append(Temp, T, ListUpdated)
; ListOrig = ListUpdated
).
Upvotes: 0
Reputation: 40768
As @repeat has already nicely shown, you should use the constraint dif/2
to describe that two terms are different. This avoids the unexpected and wrong second solution.
In addition, as always when describing lists, also consider using dcg notation: You can use the nonterminal list//1
do declaratively describe a list in such a way that it can be easily and efficiently spliced into other lists at specific positions.
Consider:
replacement([], _, _) --> [].
replacement([L|Ls], L, Rs) -->
list(Rs),
replacement(Ls, L, Rs).
replacement([L|Ls], R, Rs) --> [L],
{ dif(L, R) },
replacement(Ls, R, Rs).
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
We use the interface predicate phrase/2
to use the DCG. For example:
?- phrase(replacement([1,2,3], 3, [2,3,4]), Ls).
Ls = [1, 2, 2, 3, 4] ;
false.
It is a true relation that works in all directions. It can answer quite general questions, such as: Which item has been replaced by another list? Example:
?- phrase(replacement([1,2,3], E, [2,3,4]), [1,2,2,3,4]). E = 3 ; false.
Upvotes: 2
Reputation: 18726
To allow for multiple matches per list, use meta-predicate maplist/3
and proceed like this:
item_replacement_item_mapped(E, Es, E, Es). item_replacement_item_mapped(X, _, E, [E]) :- dif(X, E). repl(Es0,X,Xs,Es) :- maplist(item_replacement_item_mapped(X,Xs), Es0, Ess1), append(Ess1, Es).
Sample queries:
?- repl([1,2,3], 3, [2,3,4], L).
L = [1,2,2,3,4]
; false.
?- repl([x,y,x,y,x], x, [x,y,x], L).
L = [x,y,x,y,x,y,x,y,x,y,x]
; false.
Upvotes: 2