New to prolog, trying to get my head around dealing with lists of lists recursively.
Say I have a list:
prices([20, 20, 40]).
And another list (of lists):
items([ [12, 14, 23],[8, 16, 22],[18, 12, 14] ]).
I want to append an element from prices to each list in items:
items([ [12, 14, 23, 20],[8, 16, 22, 20],[18, 12, 14, 40] ]).
I'm having trouble with working through both a single list and a list of lists at the same time.
Any pointers would be appreciated.
Thanks!
It's not too difficult if you sort it out in its appropriate pieces. In prolog, a list is a list until you need to do something with an element.
item_prices(ItemPrices) :-
prices(Prices),
items(ItemLists),
item_prices(ItemLists, Prices, [], ItemPrices), !.
item_prices([ItemList|ItemLists], [Price|Prices], SoFar, ItemPrices) :-
append(ItemList, [Price], ItemPrice), % Assumes the items are lists, price is not
item_prices(ItemLists, Prices, [ItemPrice|SoFar], ItemPrices).
item_prices(_, _, ItemPrices, ItemPrices).
Then you'd just call:
item_prices(ItemPrices).
To give:
ItemPrices = [[18,12,14,40],[8,16,22,20],[12,14,23,20]]
any time you have difficulty in dealing with too much stuff at the same time, see if you can separate your concerns and deal with each separately, through an auxiliary predicate.
For instance here you could define add_to_end( Element, List, Newlist) and use it in your main predicate.
add_to_end( E, [], R):- R = ... .
add_to_end( E, [A|B], R):- R=[A|X], add_to_end( E, B, ...).
When you've mastered the basics of the language, do see what's there in the library; chances are there's already a predicate that lets you append two lists, which you could use somehow instead of writing your own specialized version of it. After all, add_to_end( E, L, R) == append_two_lists(L, [E], ...).
this is a variation on mbratch answer
item_prices(ItemPrices) :-
prices(Prices),
items(ItemLists),
item_prices(ItemLists, Prices, ItemPrices), !.
item_prices([ItemList|ItemLists], [Price|Prices], [ItemPrice|ItemPrices]) :-
append(ItemList, [Price], ItemPrice), % Assumes the items are lists, price is not
item_prices(ItemLists, Prices, ItemPrices).
item_prices([], [], []).
without an accumulator, we avoid to reverse the order.
Here is a solution, using maplist/3, handy when you must perform the same operation among matched lists elements:
items_updated(Us) :-
items(Is),
prices(Ps),
maplist(append_, Is, Ps, Us).
append_(Is, P, R) :- append(Is, [P], R).
Related
I'm trying to make this:
times([x, x], [1, 5, 9, 8], Result).
The second list is replicated by the number of elements in the first one.
The result is : [1, 5, 9, 8, 1, 5, 9, 8]
I've tried this but is not working properly:
times( [ ], _, [ ] ) :- !.
times( [ _ | List1 ], List2, Result ) :- append( [], List2, Result ),
times( List1, List2, Result ).
Thanks in advance!
Firstly, you do not need your cut in the base case. It unnecessarily prunes the solution search:
replicate([], _, []). % The empty list is the result of replicating any list by []
Then your recursive rule, replicate([_|List1], List2, Result) seems to have a reasonable format/head. But you have logic issues in the body. If you were to describe the logic, it would not make sense. In particular, you're using Result in two different places for two different meanings, and that will result in unexpected failure. The predicate just needs to be thought out logically for what it means:
Result is the replication of List2 by [_|List1] if SubResult is the replication of List2 by List1 and Result is the result of appending SubResult to List2.
Note the use of a "subresult" (SubResult) here which is distinguished from the main result (Result). It's important that these be different variables.
If you write that as Prolog, you get:
replicate([_|List1], List2, Result) :-
replicate(List1, List2, SubResult),
append(List2, SubResult, Result).
I didn't test this, but this should basically do it. You should try it and resolve any residual issues yourself as part of your Prolog learning process. I also did not consider whether there's a more effective approach to the overall problem but am just resolving your issues with your current approach.
Another would be to use maplist/2 and append/2. You can use maplist/3 to get a list of lists, then use append/2 to get your result:
replicate(List1, List2, Result) :-
length(List1, Len),
length(R1, Len),
maplist(=(List2), R1),
append(R1, Result).
With a little more thought, this can be solved using simple recursive list handling. In this case, you would recursively unify the head of the result with the head of each element in the second list, then continue this process for each element in the first list.
replicate(Rep, List, Res) :-
replicate(Rep, List, List, Res).
replicate([], _, _, []).
replicate([R|Rs], List, [X|Xs], [X|Res]) :-
replicate([R|Rs], List, Xs, Res).
replicate([R|Rs], List, [], Res) :-
replicate(Rs, List, List, Res).
We can literally substitute the second list for each element in the first, then call append/2 (SWI Prolog has one):
nreplicate( Xs, Ys, Result) :-
maplist( subst(Ys), Xs, Zs),
append( Zs, Result ).
subst(Ys, _, Ys).
I have a predict which gets first N elements:
nfirst(N, _, Lnew) :- N =< 0, Lnew = [].
nfirst(_, [], []).
nfirst(N, [X|Y], [X|Y1]) :- N1 is N - 1, nfirst(N1, Y, Y1).
It works:
% nfirst(3,[1,2,3,4,5,6],X).
% X = [1, 2, 3]
I need a predict for divide list like below:
% divide([a,b,c,d,e,f,g,h],[3,2,1,2],X).
% X = [[a,b,c],[d,e],[f],[g,h]]
The best way is using nfirst.
Very similar question to the one I answered here. Again, the trick is to use append/3 plus length/2 to "bite off" a chunk of list, per my comment above:
split_at(N, List, [H|[T]]) :- append(H, T, List), length(H, N).
If you run that, you'll see this:
?- split_at(4, [1,2,3,4,5,6,7,8], X).
X = [[1, 2, 3, 4], [5, 6, 7, 8]] ;
So this is the backbone of your program, and now you just need the usual recursive stuff around it. First, the base case, which says, if I'm out of list, I should be out of split locations, and thus out of result:
divide([], [], []).
Note that explicit base cases like this make your program more correct than something like divide([], _, _) because they will cause you to fail if you get too many split locations for your list size.
Now the recursive case is not difficult, but because split_at/3 puts two things together in a list (probably a bad choice, you could make split_at/4 as an improvement) you have to take them out, and it clouds the logic a bit here while making (IMO) a nicer API on its own.
divide(List, [Split|Splits], [Chunk|Rest]) :-
split_at(Split, List, [Chunk, Remainder]),
divide(Remainder, Splits, Rest).
This should be fairly straightforward: we're just taking a Split location, using it to chop up the List, and repeating the processing on what's left over. It seems to work as you expect:
?- divide([a,b,c,d,e,f,g,h],[3,2,1,2],X).
X = [[a, b, c], [d, e], [f], [g, h]] ;
false.
Hope this helps! Compare to the other answer, it may illuminate things.
I want to know if it's possible to get a list of numbers from nested predicates in prolog.
I'll make an example, from:
?- elements(p(f(0,5,1), k(8, f(7,3), h(6)), 5), X).
I want in X this:
X = [0,5,1,8,7,3,6,5].
Thank you if you can help me =)
Whenever you write predicates involving general term traversal, always keep in mind that such predicates will be limited in the way they can be used. Let's call your relation term_subtermnumbers/2 which relates a term to the list of numbers that occur in it as subterms in the order of their left-to-right appearance, including multiple occurrences. First, you may think of examples you gave, like
?- term_subtermnumbers(p(f(0,5,1), k(8, f(7,3), h(6)), 5), Numbers).
Numbers = [0, 5, 1, 8, 7, 3, 6, 5].
But what if you turn the query around, and ask instead:
?- term_subtermnumbers(Term, [0, 5, 1, 8, 7, 3, 6, 5]).
There are many possibilities for solutions. In fact, infinitely many. Or take a simpler query:
?- term_subtermnumbers(Term, []).
That is, all the Terms that do not contain numbers.
Since the set of solutions is infinite here, and there is no way to abbreviate that set meaningfully as answers, it really makes sense to produce in that case a special error called an instantiation error.
Some - not all - Prolog built-ins ensure this property. (=..)/2 and functor/3 are faithfully guaranteeing that property. Helas, number/1 is not. As a consequence, always use (=..)/2 or functor/3 prior to number/1, atom/1, atomic/1 and some others.
term_subtermnumbers(Term, Numbers) :-
phrase(subtermnumbers(Term), Numbers).
subtermnumbers(Term) -->
{ Term =.. [_| Args] },
( {number(Term)} -> [Term]
; args_subtermnumbers(Args)
).
args_subtermnumbers([]) --> [].
args_subtermnumbers([Arg|Args]) -->
subtermnumbers(Arg),
args_subtermnumbers(Args).
If your Prolog has append/2 and maplist/3:
elements(N, [N]) :- number(N), !.
elements(S, Ss) :- S=..[_|Es], maplist(elements, Es, Ts), append(Ts, Ss).
This worked for me:
?- elements(p(f(0,5,1), k(8, f(7,3), h(6)), 5), X), writenl(X), fail.
elements(X,X) :- integer(X).
elements([],[]).
elements([X|Y],Z) :- integer(X), elements(Y,V), Z=[X|V].
elements([X|Y],Z) :- elements(X,W), elements(Y,V), append(W,V,Z).
elements(_(|Y),Z) :- elements(Y,Z).
writenl(X) :- write(X), nl.
It gave me [0, 5, 1, 8, 7, 3, 6, 5].
Try this instead:
?- elements(p(f(0,5,1), k(8, f(7,3), h(6)), 5), X), writenl(X), fail.
elements(X,X) :- integer(X).
elements([],[]).
elements([X|Y],Z) :- integer(X), elements(Y,V), Z=[X|V].
elements([X|Y],Z) :- elements(X,W), elements(Y,V), append(W,V,Z).
elements(X,Z) :- X=..[_|Y], elements(Y,Z).
writenl(X) :- write(X), nl.
I have these two programs and they're not working as they should. The first without_doubles_2(Xs, Ys)is supposed to show that it is true if Ys is the list of the elements appearing in Xs without duplication. The elements in Ys are in the reversed order of Xs with the first duplicate values being kept. Such as, without_doubles_2([1,2,3,4,5,6,4,4],X) prints X=[6,5,4,3,2,1] yet, it prints false.
without_doubles_2([],[]).
without_doubles_2([H|T],[H|Y]):- member(H,T),!,
delete(H,T,T1),
without_doubles_2(T1,Y).
without_doubles_2([H|T],[H|Y]):- without_doubles_2(T,Y).
reverse([],[]).
reverse([H|T],Y):- reverse(T,T1), addtoend(H,T1,Y).
addtoend(H,[],[H]).
addtoend(X,[H|T],[H|T1]):-addtoend(X,T,T1).
without_doubles_21(X,Z):- without_doubles_2(X,Y),
reverse(Y,Z).
The second one is how do I make this program use a string? It's supposed to delete the vowels from a string and print only the consonants.
deleteV([H|T],R):-member(H,[a,e,i,o,u]),deleteV(T,R),!.
deleteV([H|T],[H|R]):-deleteV(T,R),!.
deleteV([],[]).
Your call to delete always fails because you have the order of arguments wrong:
delete(+List1, #Elem, -List2)
So instead of
delete(H, T, T1)
You want
delete(T, H, T1)
Finding an error like this is simple using the trace functionality of the swi-prolog interpreter - just enter trace. to begin trace mode, enter the predicate, and see what the interpreter is doing. In this case you would have seen that the fail comes from the delete statement. The documentation related to tracing can be found here.
Also note that you can rewrite the predicate omitting the member check and thus the third clause, because delete([1,2,3],9001,[1,2,3]) evaluates to true - if the element is not in the list the result is the same as the input. So your predicate could look like this (name shortened due to lazyness):
nodubs([], []).
nodubs([H|T], [H|Y]) :- delete(T, H, T1), nodubs(T1, Y).
For your second question, you can turn a string into a list of characters (represented as ascii codes) using the string_to_list predicate.
As for the predicate deleting vovels from the string, I would implement it like this (there's probably better solutions for this problem or some built-ins you could use but my prolog is somewhat rusty):
%deleteall(+L, +Elems, -R)
%a helper predicate for deleting all items in Elems from L
deleteall(L, [], L).
deleteall(L, [H|T], R) :- delete(L, H, L1), deleteall(L1, T, R).
deleteV(S, R) :-
string_to_list(S, L), %create list L from input string
string_to_list("aeiou", A), %create a list of all vovels
deleteall(L, A, RL), %use deleteall to delete all vovels from L
string_to_list(R, RL). %turn the result back into a string
deleteV/2 could make use of library(lists):
?- subtract("carlo","aeiou",L), format('~s',[L]).
crl
L = [99, 114, 108].
while to remove duplicates we could take advantage from sort/2 and select/3:
nodup(L, N) :-
sort(L, S),
nodup(L, S, N).
nodup([], _S, []).
nodup([X|Xs], S, N) :-
( select(X, S, R) -> N = [X|Ys] ; N = Ys, R = S ),
nodup(Xs, R, Ys).
test:
?- nodup([1,2,3,4,4,4,5,2,7],L).
L = [1, 2, 3, 4, 5, 7].
edit much better, from ssBarBee
?- setof(X,member(X,[1,2,2,5,3,2]),L).
L = [1, 2, 3, 5].
I'm having problem constructing a list of lists in my prolog program.
I have a predicate which gives me back a single case of a row. I have to group all the cases of this row and transform them into a list of lists. I can access them just fine but when I exit, all I'll get is the first element.
Here's the code:
sudoku3to2 :- s3to2(1).
s3to2(Line) :-
Line < 9,
Line1 is Line+1,
s3getLine(Line,0,[L]),
assert(sudoku2(Y,L])),
s3to2(Line1).
s3to2(9).
s3getLine(Line,X, , ) :-
X < 9,
X1 is X + 1,
sudoku3(Line,X, ),
s3getLine(Line,X1, , ).
s3getLine(Line,9,L,L).
sudoku3/3 will return the element at the X,Y coordinate. When I get to s3getLine(Line,9,L,L) I'll start going back. I want to keep all the elements I've gathered and not just the first one. And I'm really having trouble constructing the proper predicate calls.
findall/3 is the 'list constructor' more easily understood.
It's a builtin that list all solutions found, shaping the elements with a specified pattern. Here the pattern is really just the variable we are interested to.
I use between/3 to obtaing a correctly ordered matrix, without regard to sudoku3 rules order.
sudoku3(1, 1, a).
sudoku3(1, 2, b).
sudoku3(2, 1, c).
sudoku3(2, 2, d).
mat(M) :-
W = 2,
findall(Row,
(between(1, W, R),
findall(V, (between(1, W, C), sudoku3(R, C, V)), Row)
), M).
Result:
?- mat(M).
M = [[a, b], [c, d]].
You should change W=9.
HTH