PROLOG store nth element of list if string is found in list - prolog

What im trying to do is:
fromHistory/2
fromHistory(HL,FL)
FL is the 3rd element of the list if the list contains the word "ate"
FL is the 4th element of the list if the list contains all the words ["you","can","have"]
The predicate is supposed to loop on a list of lists HL and if one of the lists inside contains the words above, it should append the 3rd/4th element depending on the word found to FL, else it shouldn't get anything.
?- fromHistory([[i,ate,x], [you,can,have,y]], FL).
FL = [x, y] ;
false.
?- fromHistory([[this,is,a,useless,input], [i,ate,x], [another,input],
[another,useless,input], ["Ok"], [you,can,have,y]], FL).
FL = [x, y] ;
false.
x and y are not always at the end of the list but are the strings after ["ate"] and ["you","can","have"]
my attempt using the find version in here
find(X,Y,[X,Y|Tail]):-
!.
find(X,Y,[_|Tail]):-
find(X,Y,Tail).
foodFromHistory(HL1, FL):-
flatten(HL1, HL),
find(ate, FL1, HL),
find([you, can, have], FL2, HL),
FL = [FL1|FL2].
However it doesnt work with [you,can,have] and returns false, it also doesn't work on the entire list but rather on the first occurrence only.

As a rule of thumb, if you need to process a list of some things element by element, first get a very clear idea of what to do for every single element (in this case, these "elements" are input phrases) and implement and test that without worrying about the whole problem yet. So:
FL is the 3rd element of the list if the list contains the word "ate"
FL is the 4th element of the list if the list contains all the words ["you","can","have"]
This isn't a very good specification, but here is one implementation you can test and tweak in isolation from the bigger problem:
input_food([_Somebody, ate, Food], Food).
input_food(Input, Food) :-
append(_Something, [you, can, have, Food | _Rest], Input).
That is all! You have two requirements, each describing a simple pattern match on a list. The Prolog implementation can therefore be two clauses, each implementing a simple pattern match on a list.
Let's test:
?- input_food([i, ate, x], Food).
Food = x ;
false.
?- input_food([you, ate, x], Food).
Food = x ;
false.
?- input_food([ok, you, can, have, strawberries], Food).
Food = strawberries ;
false.
?- input_food([this, sentence, no, food], Food).
false.
OK, all we need to do now is to iterate over the input list and collect the foods given by input_food/2 for each input, if any. This is standard:
inputs_foods([], []).
inputs_foods([I|Is], [Food|Fs]) :-
input_food(I, Food),
inputs_foods(Is, Fs).
inputs_foods([I|Is], Fs) :-
\+ input_food(I, _Food),
inputs_foods(Is, Fs).
And it seems to mostly do what you want:
?- inputs_foods([[this,is,a,useless,input], [i,ate,x], [another,input],
[another,useless,input], ["Ok"], [you,can,have,y]], FL).
FL = [x, y] ;
false.

I dont completely understand how the prediacte should work, what about array like [some,input,i,ate,x,some,other,input], should it append x to the list ?
You could try with making your own lists like H1 = [i,ate,X|_], and H2 = [you,can,have,Y|_], then just recursively going through members of HL and comparing them and getting your solutions unifying them with X or Y.
edit :
I've made something for the [i,ate,x], now you have to make similar approach to [you,can,have,y].
The approach is to check if the list [i,ate,X] is sublist of current member of HL, if it is we can add X to our set of FL. Check if this is what you were expecting :)
fromHistory(HL,FL) :-
findAnswers(HL,[],FL).
findAnswers([],Answers,Answers).
findAnswers([H|HL],FL,Answers) :-
(isSublist([i,ate,X],H) -> append(FL,[X],FL2); FL2 = FL),
findAnswers(HL,FL2,Answers).
isSublist(SL, L) :-
append([_,SL,_],L).

A solution for your problem could be this:
solve(L,FL,FLO):-
member("ate",L),
\+consequent(L),
nth1(3,L,E),
append(FL,[E],FLO).
solve(L,FL,FLO):-
\+member("ate",L),
consequent(L),
nth1(4,L,E),
append(FL,[E],FLO).
consequent(L):-
nth1(P,L,"you"),
P1 is P+1,
nth1(P1,L,"can"),
P2 is P+2,
nth1(P2,L,"have").
fromHistory([],L,L).
fromHistory([H|T],L,FL):-
solve(H,L,FLO),
fromHistory(T,FLO,FL).
So first you check if ate is into the list and you can have is not into it. Then you can find the element you want with nth/3 and append it to the list with append/3. Similar case when you find you can have into the list and ate is not into it. You have to decide what to do when you have both ate and you can have into the list. In this implementation the predicate fails.
Query:
?- fromHistory([["you","can","have","hallo","at"],["ate","str1","str2"]],["test","aa"],L).
L = ["test", "aa", "hallo", "str2"]

Related

Prolog, how should I construct a list of list to a single list?

I want to construct a list of list to interleave each other to a single list like: coon([[1,4],[2,5],[3,6]], X) should return X=1,2,3,4,5,6. and there is a condition that each sublist should only have the same length, otherwise, it should fail such as [[q,r,y],[a,e],[c,g,t],X] shouid fail, and coon([A,B,C],[q,w,e,r,t,y]) should only return one solution, that is A=[q,r],B=[w,t],C=[e,y].
my recent approach is.
conns([],[]).
conns([[Head|Tail]|X],[Head|Y]):-
append(X,[Tail],X2),
conns(X2,Y).
conns([[]|T],A):-
conns(T,A).
It gives me multiple solutions when I try coon([A,B,C],[q,w,e,r,t,y]).
I have been trying hours to figure it out but all failed. How should I return the single list to each sub-lists that contain the same length?
Thank you so much!
:- use_module(library(clpfd),[transpose/2]).
connsx(Xss, Xs) :-
transpose(Xss, XssT),
append(XssT, Xs).
The problem you are having is with this predicate clause:
conns([[]|T],A):-
conns(T,A).
This allows solutions more general than you are wanting to define. Specifically, if I understand the problem correctly, the first argument to conns should always be a list whose elements are lists all of equal length. That would mean that if [[]|T] is the first argument and you expect conns([[]|T], A) to succeed, then T should also look like [[]|R] or []. That is, it should be a (possibly empty) list of empty lists.
If you revise the empty list case according to this constraint, your solution will work:
% The case where the first argument consists of non-empty lists
conns([[Head|Tail]|X], [Head|Y]):-
append(X, [Tail], X2),
conns(X2, Y).
% Base case in which first argument is a list of empty lists
conns([], []).
conns([[]|T], []) :-
conns(T, []).
Now when you run the query, you get this:
| ?- conns([[1,4],[2,5],[3,6]], R).
R = [1,2,3,4,5,6] ? ;
no
| ?-
As well as:
| ?- conns([A,B,C], [q,w,e,r,t,y]).
A = [q,r]
B = [w,t]
C = [e,y] ? a
no
| ?-
This solution does leave a choice point, which I'll leave as an exercise to eliminate if you wish.

Prolog predicate that extracts all words immediately following a word in a given list of words

As it says in the title, i need to get all the words after a specifc word in prolog, for example:
?- find([in, house, car, in, shop, no, more, apples, in, table], in , X).
X = [house, shop, table] ;
No
This is the code i've written so far:
find([H,H_1|_],H,[H_1]).
find([Head,Head_1|Tail], Term, [Head|Result]) :-
find(Tail, Term, Result).
After i run it, i get:
X = [house] ;
X = [in, car, shop, more, table] ;
No
There is nothing better than writing simple programs to learn a language. After you grasp the basics, you could be interested into more idiomatic approach:
find(L,W,Fs) :- findall(F, append(_,[W,F|_],L), Fs).
The main problem is probably located here:
find([H,H_1|_],H,[H_1]).
This code unifies the list with the first element after the match. You then unify the third parameter (which is here used as a "result") with a list containing the single occurrence.
Note furthermore that it is also possible that we reached the end of the list. So in that case the predicate will fail as well.
Basically there are four cases here:
we reach the end of the list, the "result" parameter should unify with the empty list;
we found the element and there is a next element (that is also a match), we perform one step and continue our search;
we found the element and there is a next element (that is not a match), we add that element and continue our search;
the head does not match, we continue our search.
We can implement these possibilities as:
find([],_,[]). % we reach the end of the list
find([H,H|T],H,T2) :- % there is a match, the successor is also a match
find([H|T],H,T2). % perform one hop
find([H,N|T],H,[N|T2]) :- % there is a match, add the next element
N \= H,
find(T,H,T2). % and continue
find([N|T],H,T2) :- % there is no match
N \= H,
find(T,H,T2). % we continue
This produces:
?- find([in, house, car, in, shop, no, more, apples, in, table], in , X).
X = [house, shop, table] ;
false.
?- find([a,b,c],c,X).
false.
?- find([a,b,c,a,d],a,X).
X = [b, d] ;
false.
?- find([a,a,b],a,X).
X = [b] ;
false.
(Yes/No are in swi-prolog true/false).

Prolog return a list which contains only elements which are equal to head of the list

Hello I would like to ask a doubt I have with the following code:
principio([],[]).
principio([H],[H]).
principio([H,_|_],[H]).
principio([H,H|C],P) :-
principio([H|C],R),P=[H|R].
I would like a way to get from:
?- principio([222,333,101,202,12,222,13,222],X).
X = [222,222,222]
But in this moment I get just the head:
X = [222]
So, to keep it clear I'd like: all successive occurrences of the first element as a list.
My doubt is what does this assignment P=[H|R] why not to put just:
principio([H,H|C],P) :-
principio([H|C],P)
Also, how would you try to modify this to get the result I asked for?
Thank you
Here is two ways how you can narrow down the problem. 1st, start from an unexpectedly failing query. 2nd, start from a query that should fail but rather succeeds.
1st Diagnose unexpected incompleteness
Determine a most specific failing query
?- principio([222,333,101,202,12,222,13,222],[222,222,222]).
false.
Generalize the query
... as much as possible. I could do this manually, or I could let Prolog do the work for me. Here I use library(diadem):
?- use_module(diadem).
true.
?- principio([222,333,101,202,12,222,13,222],[222,222,222]).? Gen.
Gen = principio([222, 333|_], [_, _|_])
; Gen = (dif(A100, B100), principio([A100, B100|_], [_, _|_]))
; ... .
In other words: Not only does your original query fail, but also this generalization fails! Here, we only insist that the first two elements are different, and that the resulting list contains at least two elements — no matter which!
?- dif(X, Y), principio([X,Y|_],[_,_|_]).
Generalize your program
:- op(950, fy, *).
* _P_0.
principio([], _/*[]*/).
principio([_H], _/*[H]*/).
principio([H,_|_],[H]).
principio([H,H|C],P) :-
* principio([H|C],R),
* P=[H|R].
The error must reside in the little remaining part of your program. No need to read any further!
The problem is that for a list starting with two different elements you only have the clause principio([H,_|_],[H]).. So this part has to be generalized somehow.
2nd Diagnose unexpected unsoundness
Another way of finding the error would be to start with the unexpected solution:
?- principio([222,333,101,202,12,222,13,222],[222]).
true. % incorrect !!
And then reduce the size of the query as much as possible.
?- principio([222,222],[222]).
true. % incorrect !!
Now, specialize your program inserting false as long as above query succeeds:
principio([],[]) : - false.
principio([H],[H]) :- false.
principio([H,_|_],[H]).
principio([H,H|C],P) :- false,
principio([H|C],R),
P=[H|R].
The remaining visible part is the culprit! We have to revise it. What it says is:
Any list starting with two elements corresponds to the list with the first element only.
principio([],[]).
principio([H],[H]).
principio([H,D|Xs], [H|Hs]) :-
dif(H,D),
principio([H|Xs],[H|Hs]).
principio([H,H|Xs],[H|Hs]) :-
principio([H|Xs],Hs).
In addition to the very nice answer provided by #false (+s(0)), I would point out the possibility to use DCGs for the task. They usually yield easily readable code when describing lists (see comments beside the grammar rules):
principio([H|T],Hs) :-
phrase(heads([H|T],H),Hs).
heads([],_H) --> % in the empty list
[]. % there's no element matching H
heads([H|Xs],H) --> % if the head of the list matches H
[H], % it's in the list
heads(Xs,H). % same for the tail
heads([X|Xs],H) --> % if the head of the list is
{dif(X,H)}, % different from H it's not in the list
heads(Xs,H). % same for the tail
Thus your example query yields the desired result:
?- principio([222,333,101,202,12,222,13,222],X).
X = [222,222,222] ? ;
no

Predicate about a list of lists

I'm trying to create a predicate that receives a list of lists and returns a list of lists containing all the unitary lists (lists whose length is 1) from the first list, however it is not working. This is what I created:
elimina_listas_nao_unitarias_lista_de_listas([[A]|T],N_List):-
length([A], 1),
N_List is [H|N_List_T],
elimina_listas_nao_unitarias_lista_de_listas(T, N_List_T).
elimina_listas_nao_unitarias_lista_de_listas([[A]|T], N_List):-
length([A], X),
X > 1,
elimina_listas_nao_unitarias_lista_de_listas(T, N_List2).
Thi is what it should do:
elimina_listas_nao_unitarias_lista_de_listas([[1,2],[1,2,3],[3]], [3])
elimina_listas_nao_unitarias_lista_de_listas([[1,2],[1,2,3],[3,4,5]], [])
It is retuning false currently everytime
Let's take a look at your first rule. The first goal always succeeds, since you are asking if a list with a single element is of length 1. Just try it at the prompt:
?- length([A], 1).
true
Instead, you probably want to have a variable without the brackets in the head of the first list (e.g. [L|Ls]) and ensure that it is a list of length 1:
?- length(L,1).
L = [_A]
The same goes for the first list in the head of your second rule and its first goal. In your second goal you are trying to evaluate [H|N_List_T] as an arithmetic expression with is/2 such that N_List holds the value. Besides the fact that this doesn't make sense, you can try that at the prompt and see how this goal can't succeed:
?- N_List is [H|N_List_T].
ERROR!!
TYPE ERROR- string must contain a single character to be evaluated as an arithmetic expression: expected evaluable term, got [_131245|_131246]
Instead, you want to unify the two terms:
?- N_List = [H|N_List_T].
N_List = [H|N_List_T]
However, you can get rid of this goal entirely if you write [H|N_List_T] as the second argument in the head of the rule. Additionally, you might want the unitary list L in the head of the second list instead of the variable H. Furthermore you are missing a case, namely the first list being []. In that case the second list is empty as well, since the empty list clearly does not contain any unitary lists. Finally, I would note that it might enhance the readability of your code if you picked a somewhat simpler and more declarative name, say listas_unitarias/2. Putting all this together, you might end up with a predicate like this:
listas_unitarias([],[]).
listas_unitarias([L|Ls],[L|Ss]) :-
length(L,1),
listas_unitarias(Ls,Ss).
listas_unitarias([L|Ls],Ss) :-
length(L,X),
dif(X,1),
listas_unitarias(Ls,Ss).
Your second example query yields the desired result
?- listas_unitarias([[1,2],[1,2,3],[3,4,5]],U).
U = []
For your first example query the result is slightly different:
?- listas_unitarias([[1,2],[1,2,3],[3]], U).
U = [[3]] ? ;
no
The only unitary list is in a list itself. That would make more sense, since the first argument might contain more than one such list. Consider the following case:
?- listas_unitarias([[1],[2,3],[4],[]],U).
U = [[1],[4]] ? ;
no
However, if you meant to get the unitary lists one at a time, the predicate would look slightly different:
listas_unitarias2([L|_Ls],L) :-
length(L,1).
listas_unitarias2([_L|Ls],U) :-
listas_unitarias2(Ls,U).
As would the results of the queries:
?- listas_unitarias2([[1,2],[1,2,3],[3]], U).
U = [3] ? ;
no
?- listas_unitarias2([[1],[2,3],[4],[]],U).
U = [1] ? ;
U = [4] ? ;
no
Especially your second example query: It would fail instead of producing the empty list as a solution:
?- listas_unitarias2([[1,2],[1,2,3],[3,4,5]],U).
no
?- listas_unitarias2([[1,2],[1,2,3],[3,4,5]],[]).
no
EDIT: As pointed out by #false in the comments the combined use of length/2 and dif/2 in the third rule doesn't terminate for [_,_|_] so the query
?- listas_unitarias([[1],[_,_|_],[2],[3,4]],U).
U = [[1],[2]] ? ;
U = [[1],[2]] ? ;
...
does not terminate as well. However, it is reasonable to expect termination in this case, since a list headed by two elements certainly can't be unitary. So, instead of using length/2 you might consider describing the four cases that cover all possibilities. 1) If the first list is empty so is the second list. 2) If the head of the first list is [] it's not in the second list. 3) If the head of the first list is [A] it is in the second list. 4) If the head of the first list has at least two elements it's not in the second list.
listas_unitarias([],[]). % case 1)
listas_unitarias([[]|Ls],Ss) :- % case 2)
listas_unitarias(Ls,Ss).
listas_unitarias([[A]|Ls],[[A]|Ss]) :- % case 3)
listas_unitarias(Ls,Ss).
listas_unitarias([[_,_|_]|Ls],Ss) :- % case 4)
listas_unitarias(Ls,Ss).
With this version the above query terminates after finding the only solution:
?- listas_unitarias([[1],[_,_|_],[2],[3,4]],U).
U = [[1],[2]]
The other queries from above yield the same results:
?- listas_unitarias([[1,2],[1,2,3],[3,4,5]],U).
U = []
?- listas_unitarias([[1,2],[1,2,3],[3]], U).
U = [[3]]
?- listas_unitarias([[1],[2,3],[4],[]],S).
S = [[1],[4]]

Prolog: Doubling the value of each element in a list of lists and returning a single list

I need write a set of clauses that take a list of integer lists and return a single list with all the elements doubled.
For example:
?- double([[1,2],[3]], X).
Yes
X = [2,4,6]
I have a set of clauses called mega_append that return a single list from a list of lists.
For example:
?- mega_append([[1,2],[3]], X).
Yes
X = [1,2,3]
Here is my progress (m_a is short for mega_append):
double([],[]).
double(List,[H1|T1]) :-
m_a(List,[H2|T2]),
H1 is 2 * H2,
double(T2, T1).
I'll try to explain how I thought it would work. I flatten the first list and split it up into a head and a tail (H2 and T2). I split the second list into a head and a tail (H1 and T1). I check to make sure that H1 (the doubled value) is equal to 2 times H2 (the original value). If it is then I check the rest of the list. Eventually if they all match correctly I should be left with two empty lists which should match the first clause and return yes.
It works when there is only a single value (for example: double([[1]], X)). Can anyone offer any insight into what I am doing wrong? Is my logic or code incorrect?
Your problem is that T2 is a single list so List after the recursive call is not a list of lists.
To solve this you can first use mega_append to flatten the list and then use an auxiliary predicate to work on the flattened list.
I.e. the double will look like this:
double([],[]).
double(List,X) :-
m_a(List,FList),
double_aux(List, FList).
Edit:
Here is a way to only use one clause since you want to see one.
I recommend using an auxiliary predicate.
double([],[]).
double([[]],[]).
double(List,[H1|T1]) :-
mega_append(List,[H2|T2]),
H1 is 2 * H2,
double([T2], T1).
Using clpfd, we define the dcg nonterminal double//1 like this:
:- use_module(library(clpfd)).
double([]) --> [].
double([D|Ds]) --> {DD #= D*2}, [DD], double(Ds).
Let's run some queries—using phrase/2,
apply:foldl/4, and nonterminal double//1:
:- use_module(library(apply)).
?- phrase(foldl(double,[[1,2],[3]]),Xs).
Xs = [2,4,6].
?- phrase(foldl(double,[[A,B],[C]]),[2,4,6]).
A = 1, B = 2, C = 3.
Want more examples using phrase/[2,3]?
Read this SICStus Prolog manual page!

Resources