recipe function in prolog - list

Say you have recipes in prolog saved as
step(meal name, stepnumber, stepexaplanation, ingredients (list) , utensils).
So for every meal name you have multiple steps with all the explanations to make the meal. How would you then make a function that calls getStepText(X,Y,Z).? So if you ask:
?- getStepText('pancakes',2,Y).
It will return the text of the second step for the meal pancakes.

step('pancakes', 1, 'mix butter and sugar in a bowl', [butter, sugar], [bowl]).
step('pancakes', 2, 'add eggs', [eggs], []).
step('pancakes', 3, 'mix flour and bakingpowder', [flour, baking_powder], []).
getStepText(Dish,Num,Text):-
step(Dish, Num, Text, _, _).
?- getStepText('pancakes',2,Y).
Y = 'add eggs' ;
false.
If you are using SWI Prolog you can use the inbuild predicate findall/3 to find all of your steps. Don't forget to sort them.
getSteps(X,S):-
findall(N,step(X,N,_,_,_),Bag),
sort(Bag,S).
?- getSteps('pancakes',Y).
Y = [1, 2, 3] ;
false.
If you don't want to use findall/3 you can define a helper predicate getSteps/3. This predicate assumes all steps are incremental and start with 1.
getSteps(X,S):-
getSteps(X,S,1).
getSteps(X, [], N):-
\+ step(X, N, _, _, _).
getSteps(X, [N|T], N):-
step(X, N, _, _, _),
NN is N+1,
getSteps(X, T, NN).
?- getSteps('pancakes',Y).
Y = [1, 2, 3] ;
false.
Explanation: you basically run a counter N from 1 (set when calling) until you can't find any step with the current number N. The first rule tests for the end: if you don't have a step for the current N "return" the empty list [].
Otherwise when you find a step with the number N, try to find the steplist T for the next value NN (which is N+1) and - once found - put N on top of the list T.

Related

How can I take input and make a list with it on Prolog?

I'm doing homework for Basic Artificial Intelligence, and the problem is, "Make a Prolog program that can read 2 lists of numeric values and
concatenate them", so I'm a total noob and I have no idea how to take a input from Prolog and put it on a list one by one
%I have only make the while func so that you
%put the total numbers you want on the list
p_while(0) :- !.
p_while(N) :-
N > 0,
N1 is N - 1,
read(Num),
p_while(N1).
There is a standard predicate called append/3. You can use it to read two lists of numerical values from the prompt and concatenate them, like this:
?- append([1,2], [3,4], L).
L = [1, 2, 3, 4].
You can use listing/1 to see how append/3 is defined:
?- listing(append/3).
lists:append([], L, L).
lists:append([H|T], L, [H|R]) :-
append(T, L, R).
true.

Prolog predicate that will check if a list A is a list of the prefix sums of a list D

So I'm very new to prolog and have to write a predicate that is satisfiable when an integer list D is the list of prefix sums of a list A.
sums(A, D)
So for example,
sums([4,11,1,-3,8], [4,15,16,13,21]) is satisfiable
I have written this predicate over a dozen different ways to no avail. This is what I currently have written.
sums([], []).
sums([A], [A]).
sums([A|[B|C]], [A|[E|F]]) :- TOTAL is A + B, E = TOTAL, sums([C], [F]).
This somewhat works, in that it will check that the first values of each list are equal, and also check that the second element in the list is correct in that it should be 15. I understand why it works incorrectly in this way, but I am having trouble coming up with how to write it differently, in the correct way.
I have since changed the code to,
sumrunner(L, S) :- sumrunner(L, S, 0).
sumrunner([], [], _).
sumrunner([A], [A], _).
sumrunner([A|B], [C|D], TOTAL) :- TOTAL is TOTAL + A, TOTAL = C,sumrunner(B, D, TOTAL).
However, now it just says false for all cases except for when the two lists are empty, and when the lists both contain one element and they are both equal to each other.
You should learn more about list notation: [A|[B|C]] can be written as [A,B|C] for example. It is now clearer that C is the tail of the list, and thus, is a list itself! Therefore, when you write sums([C], [F]), you are wrapping C and F into a list, even though they are already lists, which is your problem.
If we fix this and run your predicate, we get this:
?- sums([4,11,1,-3,8],Z).
Z = [4, 15, 1, -2, 8]
It is still wrong as you can see. The main problem is that, the recursive call sums in the third rule expresses that the prefix sums of the tail of a list are the tail of the prefix sums of the list, which is wrong because those prefix sums depend on the previous element!
To solve this, you need to introduce an extra argument to maintain the sum value throughout recursive calls:
:- use_module(library(clpfd)).
prefix_sums(L, D) :-
prefix_sums(L, 0, D).
prefix_sums([], _, []).
prefix_sums([H|T], S, [S1|T2]) :-
S1 #= H + S,
prefix_sums(T, S1, T2).
Using library(clpfd), we get the behaviour we expect:
?- prefix_sums([4,11,1,-3,8],Z).
Z = [4, 15, 16, 13, 21].
But also the reverse behaviour:
?- prefix_sums(Z, [4,15,16,13,21]).
Z = [4, 11, 1, -3, 8].
And also correct behaviour with even less information:
?- prefix_sums([A,B,C],Z).
Z = [A, _7964, _7970],
B+A#=_7964,
C+_7964#=_7970.
?- prefix_sums(X,Z).
X = Z, Z = [] ;
X = Z, Z = [_7122],
_7122 in inf..sup ;
X = [_7452, _7458],
Z = [_7452, _7482],
_7458+_7452#=_7482 ;
X = [_7770, _7776, _7782],
Z = [_7770, _7806, _7812],
_7776+_7770#=_7806,
_7782+_7806#=_7812 ;
…
Your code must be simplified a lot:
sums(L, S) :- sumrunner(L, S, 0).
sumrunner([], [], _).
sumrunner([A|B], [C|D], TOTAL) :- C is TOTAL + A, sumrunner(B, D, C).
?- sums([4,11,1,-3,8], [4,15,16,13,21]).
true.
?- sums([4,11,1,-3,8], [4,15,16,14,21]).
false.
The expression C is TOTAL + A both checks the requirements and update the accumulator for the recursive step.

Excluding all occurrences of the minimum number in a list

As a Prolog newbie, I try to define a predicate filter_min/2 which takes two lists to determine if the second list is the same as the first, but with all occurrences of the minimum number removed.
Sample queries with expected results:
?- filter_min([3,2,7,8], N).
N = [3,7,8].
?- filter_min([3,2,7,8], [3,7,8]).
true.
I tried but I always get the same result: false. I don't know what the problem is. I need help!
Here is my code:
filter_min(X,Y) :-
X == [],
write("ERROR: List parameter is empty!"),
!;
min_list(X,Z),
filter(X,Y,Z).
filter([],[],0).
filter([H1|T1],[H2|T2],Z) :-
\+ number(H1),
write("ERROR: List parameter contains a non-number element"),
!;
H1 \= Z -> H2 is H1, filter(T1,T2,Z);
filter(T1,T2,Z).
There are a couple of problems with your code:
filter([],[],0). will not unify when working with any list that does not have 0 as its minimum value, which is not what you want. You want it to unify regardless of the minimum value to end your recursion.
The way you wrote filter([H1|T1],[H2|T2],Z) and its body will make it so that the two lists always have the same number of elements, when in fact the second one should have at least one less.
A correct implementation of filter/3 would be the following:
filter([],[],_).
filter([H1|T1],L2,Z):-
\+ number(H1),
write("ERROR: List parameter contains a non-number element"),
!;
H1 \= Z -> filter(T1,T2,Z), L2 = [H1|T2];
filter(T1,L2,Z).
A bounty was offered...
... for a pure solution that terminates for (certain) cases where neither the length of the first nor of the second argument is known.
Here's a candidate implementation handling integer values, built on clpfd:
:- use_module(library(clpfd)).
filter_min(Xs,Ys) :-
filter_min_picked_gt(Xs,_,false,Ys).
filter_min_picked_gt([] ,_,true ,[]).
filter_min_picked_gt([Z|Xs],M,Picked,[Z|Zs]) :-
Z #> M,
filter_min_picked_gt(Xs,M,Picked,Zs).
filter_min_picked_gt([M|Xs],M,_,Zs) :-
filter_min_picked_gt(Xs,M,true,Zs).
Some sample queries:
?- filter_min([3,2,7,8],[3,7,8]).
true ; false. % correct, but leaves choicepoint
?- filter_min([3,2,7,8],Zs).
Zs = [3,7,8] ; false. % correct, but leaves choicepoint
Now, some queries terminate even though both list lengths are unknown:
?- filter_min([2,1|_],[1|_]).
false. % terminates
?- filter_min([1,2|_],[3,2|_]).
false. % terminates
Note that the implementation doesn't always finitely fail (terminate) in cases that are logically false:
?- filter_min([1,2|_],[2,1|_]). % does _not_ terminate
For a Prolog newbie, better start with the basics. The following works when first argument is fully instantiated, and the second is an uninstantiated variable, computing the result in one pass over the input list.
% remmin( +From, -Result).
% remmin([],[]). % no min elem to remove from empty list
remmin([A|B], R):-
remmin(B, A, [A], [], R). % remove A from B to get R, keeping [A]
% in case a smaller elem will be found
remmin([C|B], A, Rev, Rem, R):-
C > A -> remmin(B, A, [C|Rev], [C|Rem], R) ;
C==A -> remmin(B, A, [C|Rev], Rem, R) ;
C < A -> remmin(B, C, [C|Rev], Rev, R).
remmin([], _, _, Rem, R) :- reverse(Rem, R).
First, we can get the minimum number using the predicate list_minnum/2:
?- list_minnum([3,2,7,8],M).
M = 2.
We can define list_minnum/2 like this:
list_minnum([E|Es],M) :-
V is E,
list_minnum0_minnum(Es,V,M).
list_minnum0_minnum([],M,M).
list_minnum0_minnum([E|Es],M0,M) :-
M1 is min(E,M0),
list_minnum0_minnum(Es,M1,M).
For the sake of completeness, here's the super-similar list_maxnum/2:
list_maxnum([E|Es],M) :-
V is E,
list_maxnum0_maxnum(Es,V,M).
list_maxnum0_maxnum([],M,M).
list_maxnum0_maxnum([E|Es],M0,M) :-
M1 is max(E,M0),
list_maxnum0_maxnum(Es,M1,M).
Next, we use meta-predicate tfilter/3 in tandem with dif/3 to exclude all occurrences of M:
?- M=2, tfilter(dif(M),[2,3,2,7,2,8,2],Xs).
Xs = [3,7,8].
Put the two steps together and define min_excluded/2:
min_excluded(Xs,Ys) :-
list_minnum(Xs,M),
tfilter(dif(M),Xs,Ys).
Let's run some queries!
?- min_excluded([3,2,7,8],Xs).
Xs = [3,7,8].
?- min_excluded([3,2,7,8,2],Xs).
Xs = [3,7,8].

Prolog: How to remove every second element of a list

I need to write a program in Prolog that should remove every second element of a list. Should work this: [1,2,3,4,5,6,7] -> [1,3,5,7]
so far I have this, but it just returns "false".
r([], []).
r([H|[T1|T]], R) :- del(T1,[H[T1|T]], R), r(R).
del(X,[X|L],L).
del(X,[Y|L],[Y|L1]):- del(X,L,L1).
This is pretty much Landei's answer in specific Prolog syntax:
r([], []).
r([X], [X]).
r([X,_|Xs], [X|Ys]) :- r(Xs, Ys).
The second predicate is not required.
Alternative solution using foldl/4:
fold_step(Item, true:[Item|Tail], false:Tail).
fold_step(_Item, false:Tail, true:Tail).
odd(List, Odd) :-
foldl(fold_step, List, true:Odd, _:[]).
Usage:
?- odd([1, 2, 3, 4, 5, 6, 7], Odd).
Odd = [1, 3, 5, 7]
The idea is to go through the list, while keeping "odd/even" flag and flipping its value (false -> true, true -> false) on each element. We also gradually construct the list, by appending those elements which have "odd/even" flag equal to true, and skipping others.
This fine answer by #code_x386 utilizes difference-lists and foldl/4.
Let's use only one fold_step/3 clause and make the relation more general, like so:
fold_step(X, [X|Xs]+Ys, Ys+Xs).
list_odds_evens(List, Odds, Evens) :-
foldl(fold_step, List, Odds+Evens, []+[]).
Sample queries:
?– list_odds_evens([a,b,c,d,e,f], Odds, Evens).
Evens = [b,d,f], Odds = [a,c,e]
?– list_odds_evens([a,b,c,d,e,f,g], Odds, Evens).
Evens = [b,d,f], Odds = [a,c,e,g]
Edit
Why not use one clause less and do away with predicate fold_step/3?
lambda to the rescue!
:- use_module(library(lambda)).
list_odds_evens(List, Odds, Evens) :-
foldl(\X^([X|Xs]+Ys)^(Ys+Xs)^true, List, Odds+Evens, []+[]).
Another possibility is to use DCGs, they are usually a worthwhile consideration when describing lists:
list_oddindices(L,O) :-
phrase(oddindices(L),O). % the list O is described by oddindices//1
oddindices([]) --> % if L is empty
[]. % O is empty as well
oddindices([X]) --> % if L has just one element
[X]. % it's in O
oddindices([O,_E|OEs]) --> % if L's head consists of at least two elements
[O], % the first is in O
oddindices(OEs). % the same holds for the tail
This is certainly less elegant than the solutions using foldl/4 but the code is very easily readable, yet it solves the task described by the OP and works both ways as well:
?- list_oddindices([1,2,3,4,5,6,7],O).
O = [1, 3, 5, 7] ;
false.
?- list_oddindices(L,[1,3,5,7]).
L = [1, _G4412, 3, _G4418, 5, _G4424, 7] ;
L = [1, _G4412, 3, _G4418, 5, _G4424, 7, _G4430] ;
false.
I have no Prolog here to try it out, and I got a little bit rusty, but it should be along the lines of
r([]) :- [].
r([X]) :- [X].
r([X,Y|Z]) :- R=r(Z),[X|R].
[Edit]
Of course pad is right. My solution would work in functional languages like Haskell or Erlang:
--Haskell
r [] = []
r [x] = [x]
r (x:_:xs) = x : (r xs)
In Prolog you have to "pull" the right sides into the argument list in order to trigger unification.
I just needed a function like this and took a more "mathematical" approach:
odds(Xs, Ys) :- findall(X, (nth1(I,Xs,X), I mod 2 =:= 1), Ys).
It doesn't work both ways like some of the other fine answers here, but it's short and sweet.

How do I find the longest list in a list of lists?

I have a list of lists, and I need to find the longest one of them. If there are more than one with the same length it's the same which it returns. Thanks.
Here is a general predicate that scans a list to find a single member defined by a given goal.
select_element(Goal, [Head | Tail], Selected) :-
select_element(Goal, Tail, Head, Selected).
select_element(_Goal, [], Selected, Selected).
select_element(Goal, [Head | Tail], Current, FinalSelected) :-
call(Goal, Head, Current, Selected),
select_element(Goal, Tail, Selected, FinalSelected).
Lets say you define a predicate
get_bigger_number(N1, N2, N) :-
N is max(N1, N2).
Now you can execute:
?- select_element(get_bigger_number, [5, 1, -2, 10, 3.2, 0], Selected).
Selected = 10
So all you need to do now is define a predicate get_longer_list(L1, L2, L),
and use it instead of get_bigger_number/3.
Of course, using a general predicate like select_element/3 might not be very efficient. For example, you should try to avoid calculating the length of the same list several times, because this calculation is slow in Prolog (at least if implemented in Prolog in the standard way).
Please consider my aproach.
longest([L], L) :-
!.
longest([H|T], H) :-
length(H, N),
longest(T, X),
length(X, M),
N > M,
!.
longest([H|T], X) :-
longest(T, X),
!.
Then you can consult it:
?- longest([[1]], N).
N = [1] ;
?- longest([[1],[2]], N).
N = [2] .
?- longest([[1],[2], [3,3,3], [2]], N).
N = [3, 3, 3] ;
?- longest([[1],[2], [3,3,3], [2]], N).
N = [3, 3, 3].
?- longest([[1],[2], [3,3,3], [2], [4,4,4,4]], N).
N = [4, 4, 4, 4] .
?- longest([[1],[2], [3,3,3], [2], [4,4,4,4]], N).
N = [4, 4, 4, 4] ;
Greets!
We define longest/2 based on meta-predicate max_of_by/3 used in tandem with length/2:
longest(Xss,Ys) :-
max_of_by(Ys,Xss,length).
Sample queries:
?- longest([[1],[2]],Xs). % we expect multiple solutions
Xs = [1]
; Xs = [2]. % we _get_ multiple solutions
?- longest([[2,1,3],[7,5],[1,8,2,3,1],[2,7,1,4]],Xs).
Xs = [1,8,2,3,1]. % succeeds deterministically
Here is another approach that is efficient and easy to understand. The idea is to find the lengths of all lists in the list, use max_list to get the length of the longest list, and then find a list that is that long. This has the benefit that it will return all lists of the longest length.
lengths([],[]).
lengths([H|T], [LH|LengthsT]) :-
length(H, LH),
lengths(T, LengthsT).
lengthLongest(ListOfLists, Max) :-
lengths(ListOfLists, Lengths),
max_list(Lengths, Max).
longestList(ListOfLists, Longest) :-
lengthLongest(ListOfLists, Len),
member(Longest, ListOfLists),
length(Longest, Len).
% Correct again.
longest(LL,LX) :-
findmax(Len,(append(_,[L|_],LL),length(L,Len)),MaxLen),
append(_,[LX|_],LL),
length(LX,MaxLen).
findmax(V,P,Max) :-
findall(V,P,L),
max(L,Max).
max([N],N) :- !.
max([N|R],Max) :-
max(R,Max2),
max3(N,Max2,Max).
max3(N,Max2,N) :- N > Max2,!.
max3(N,Max2,Max2).
To have the length of longest list:
%sample: longest([[2,1,3],[7,5],[1,8,2,3,1],[2,7,1,4]],L,LEN).
longest([L], L, _) :-
!.
longest([H|T], H, _) :-
length(H, N),
longest(T, X, N),
length(X, M),
N > M,
!.
longest([_|T], X, LEN) :-
length(X, LEN),
longest(T, X, LEN),
!.