I can't figure out how to create a list containing the elements that have been used to form a sentence using a defined DCG.
Suppose we have the following DCGs:
father --> [Peter].
mother --> [Isabel].
child --> [Guido].
child --> [Claudia].
verb --> [is].
relation --> [father, of].
relation --> [mother, of].
pronoun --> [he].
pronoun --> [she].
adjective --> [a, male].
adjective --> [a, female].
s --> father, verb, relation, child.
s --> mother, verb, relation, child.
s --> pronoun, verb, adjective.
Sentences can be queried as follows:
phrase(s, [peter, is, father, of, guido]), phrase(s, [he, is, a, male]). which returns true.
How can I create and maintain a list of the elements of this executed sentences in order to get false when executing the following sentences (because Peter is a male, notice the she instead of he):
phrase(s, [peter, is, father, of, guido]), phrase(s, [she, is, a, female]).
This question uses the same example as here.
The proper interface to DCGs is via phrase/2, a simplified version of phrase/3:
?- phrase(s, X).
X = [_8304, is, father, of, _8328] ;
X = [_8304, is, father, of, _8328] ;
X = [_8304, is, mother, of, _8328] ;
% etc
The _8404 variables come from rules like father --> [Peter]. because Peter in there is also a variable (variables start with _ or an upper case letter. You can fix this by escaping the atom as 'Peter' - see also the other question you asked).
The first name of phrase is the DCG rule, the second argument is the list. When you use a particular list as the second argument, the answer substitution is empty and Prolog just reports that it could derive the list. In my example, I used the variable X and obtained possible substitutions for it, that can be derived.
Constraints can be added as goals enclosed in curly brackets:
dupnum(X) -->
{ member(X, [0,1,2,3,4,5,6,7,8,9]) },
[X,X].
leads to
?- phrase(dupnum(X), Y).
X = 0,
Y = [0, 0] ;
X = 1,
Y = [1, 1] ;
% etc
The example also shows that DCG productions can have arguments which you could use to propagate a parse tree or some general parsing context.
To address the extra question that was added later: you can pass information around by adding additional arguments but you need to describe the sequence of sentences. The example phrase(s, [peter, is, father, of, guido]), phrase(s, [he, is, a, male]) should succeed, what should not succeed is uttering the two sentences after each other phrase(ss, [peter, is, father, of, guido, '.', he, is, a, male,'.']) (Or it might succeed, leaving "he" as a reference to someone we do not know. It all depends on how strict we are with the context. ).
To do this properly, we need to jump quite a number of hoops. First we need to add parsing information to the DCG rules. For example, np(np([A,O],object)) --> %... will parse an article followed by an object into a structure np([A,O],object). Let's parse ['a', 'male'] and [guido] with it:
?- phrase(np(NP), [a, male]).
NP = np([article(a), object(male, male)], object) ;
false.
?- phrase(np(NP), [guido]).
NP = np([name(guido, male)], name) ;
false.
The first argument of np is a list because other np rules have only on component. Note that we added the gender as an attribute to name and object. In other languages, for example French, the article also has a gender that needs to agree with the object but in English we can set this aside. More sophisticated implementations would also take into account if the object is in singular or plural (the same goes for the different modes of a verb).
For verbs, we need to distinguish how many object noun phrases they need. This is done by the checks is_transitive/1, is_intransitive/1 and is_bitransitive/1.
Finding a good solution for demonstrative pronouns is hard: the pronoun does not need to refer to the previous subject, for example in "Gaile is married to Peter. He is older than her.". It doesn't even need to refer to the last sentence at all, for example in "Peter is where he wants to be.". This means that a) you should decide beforehand, which cases you actually want to cover and b) it is best to make these decisions in a second parsing run, when you have the full structured information. This mirrors the linguistic distinction between syntactic, semantic and pragmatic reasoning, where I would categorize the problem you would like to solve as pragmatic, which depends on the other two steps.
My solution here just incorporates the particular decision you wanted to make into a single parsing run, at the cost of readability of the ss DCG rule: we add an additional argument that collects the sentences that have been parsed already, a so called accumulator. When we start parsing, the history is empty which is reflected by the rule ss(S) --> ss(S,[]).. For the actual rules, we need to distinguish if the current sentence starts with a demonstrative pronoun or not. In the first case we need resolve it, which we do here by looking at possible noun phrases in the previous sentence that agree in gender. With this machinery in place, we can parse the sentence [peter, is, a father, '.', he, is a father,'.']:
?- phrase(ss(Tree), [peter,is,a,father,'.', he, is, a, father, '.']).
Tree = [s(np([name(peter, male)], name), vp([verb(is), np([article(a), object(father, male)], object)])), s(np([pronoun(he, male)], dpronoun), vp([verb(is), np([article(a), object(..., ...)], object)]))] ;
but we cannot parse [peter,is,a,father,'.', she, is, a, father, '.']:
?- phrase(ss(Tree), [peter,is,a,father,'.', she, is, a, father, '.']).
false.
In a proper semantic/pragmatic analysis, we would enrich the pronoun phrase with the actual referenced noun phrase but this would be done as a rewriting of the original parse tree. Here is the code for this:
%%%% utility predicates
% gender_of(X,Y) is true if X is the gender of the syntax tree node Y
gender_of(X,name(_,X)).
gender_of(X,pronoun(_,X)).
gender_of(X,object(_,X)).
gender_of(G,np([X],_)) :-
gender_of(G,X).
gender_of(G,np([_,X],_)) :-
gender_of(G,X).
% nps_of(X,Y) is true if X is the list of nps occurring in the syntax tree node Y
nps_of([],vp([_])).
nps_of([NP],vp([_,NP])).
nps_of([NP|Rest],s(NP,VP)) :-
nps_of(Rest, VP).
% nountype_of(X,Y) is true if X is the type of the np node Y
nountype_of(X, np(_,X)).
% is_intransitive(X) is true if the verb X does not require an object phrase
is_intransitive(is).
is_intransitive(walk).
% is_transitive(X) is true if the verb X requires an object phrase
is_transitive(is).
% is_bitransitive(X) is true if the verb X requires two object phrases
is_bitransitive(is).
%%%% DCG rules
% name are distinct from objects because they do not require articles
name(name(peter,male)) --> [peter].
name(name(isabel,female)) --> [isabel].
name(name(guido,male)) --> [guido].
name(name(claudia,female)) --> [claudia].
% nouns that require an article
object(object(mother,female)) --> [mother].
object(object(father,male)) --> [father].
object(object(male,male)) --> [male].
object(object(female,female)) --> [female].
% verbs
verb(verb(is)) --> [is].
verb(verb(walk)) --> [walks].
% pronouns
pronoun(pronoun(he,male)) --> [he].
pronoun(pronoun(she,female)) --> [she].
% articles
article(article(a)) -->
[a].
article(article(the)) -->
[the].
% noun phrases
np(np([A,O],object)) -->
article(A),
object(O).
np(np([N],name)) -->
name(N).
np(np([PN], dpronoun)) -->
pronoun(PN).
% verb phrases
vp(vp([V,NP])) -->
verb(V),
{ V = verb(Name), is_transitive(Name) },
np(NP).
vp(vp([V])) -->
verb(V),
{ V = verb(Name), is_intransitive(Name) }.
end -->
['.'].
% a single sentence
s(s(NP,VP)) -->
np(NP),
vp(VP),
end.
% a list of sentences, with accumulator
ss([],_Acc) -->
[].
ss([S|Sentences],[]) -->
s(S),
ss(Sentences, [S]).
ss([S|Sentences], [LastS | Acc]) -->
{ S = s(np([Pronoun], dpronoun),_) },
s(S),
{ gender_of(G, Pronoun), nps_of(LastNPS, LastS), member(LNP, LastNPS), gender_of(G,LNP) },
ss(Sentences, [S, LastS | Acc]).
ss([S|Sentences], [LastS | Acc]) -->
{ S = s(NP,_), nountype_of(NT,NP), dif(NT,dpronoun) },
s(S),
ss(Sentences, [S, LastS | Acc]).
% wrapper of ss with empty accumulator
ss(S) -->
ss(S,[]).
Related
I'm new to Prolog. I managed to learn C and Java relatively quickly but and Prolog is giving me a lot of trouble. My trouble is understanding lists and writing functions? For example. We have this automaton:
I can do this task in C and Java, no problems. But the course wants Prolog. With my current knowledge I could do things like this:
% 1. Check whether all integers of the list are < 10.
less_than_10([]).
less_than_10([Head|Tail]) :-
Head < 10,
less_than_10(Tail).
Just so you know where my knowledge is at. Very basic. I did read the list chapter in Learn Prolog Now but it's still confusing me. They gave us a hint:
Every node should be presented like:
delta(1, d, 2)
% or
alpha(2, a, 2)
They also told us to pass the list in questions to a predicate that returns true if the list fits the automaton and false if not:
accept([d,a,b,a,b,b,b,c,d,c]).
The output is true.
Where to go from here? I'm guessing the first step is to check if the Head of the list is 1. How do I do that? Also, should I add every node as fact into the knowledge base?
So that's pretty easy. Super-direct, much more than if you were using C or Java.
Let's write an interpreter for this graph that:
Is given a list of named transitions ;
Walks the transitions using the given graph along a path through that graph ;
Accepts (Succeeds) the list if we end up at a final state ;
Rejects (Fails) the list if we do not ;
And.. let's say throws an exception if the list cannot be generated by the given graph.
Prolog gives us nondeterminism for free in case there are several paths. Which is nice.
We do not have an class to describe the automaton. In a sense, the Prolog program is the automaton. We just have a set of predicates which describe the automaton via inductive definitions. Actually, if you slap a module definition around the source below, you do have the object.
First describe the graph. This is just a set of Prolog facts.
As required, we give the transitions (labeled by atoms) between nodes (labeled by integers), plus we indicate which are the start and end nodes. There is no need to list the nodes or edges themselves.
delta(1,d,2).
delta(2,a,2).
delta(2,b,2).
delta(2,d,4).
delta(2,e,5).
delta(2,c,3).
delta(3,d,6).
delta(6,c,5).
start(1).
end(4).
end(5).
A simple database. This is just one possible representation of course.
And now for the graph walker. We could use Definite Clause Grammars here because we are handling a list, but lets' not.
First, a predicate which "accepts" or "rejects" a list of transitions.
It looks like:
% accepts(+Transitions)
It starts in a start state, then "walks" by removing transitions off the list until the list is empty. Then it checks whether it is at an end state.
accepts(Ts) :- % accept the list of transitions if...
start(S), % you can accept the list starting
accepts_from(S,Ts). % from a start state
accepts_from(S,[T|Ts]) :- % accepts the transitions when at S if...
delta(S,T,NextS), % there is a transition S->NextS via T
accepts_from(NextS,Ts). % and you can accept the remaining Ts from NextS. (inductive definition)
accepts_from(S,[]) :- % if there is no transition left, we accept if...
end(S). % we are a final state
Ah, we wanted to throw if the path was impossible for that graph. So a little modification:
accepts(Ts) :- % accept the list of transitions if...
start(S), % you can accept the list starting
accepts_from(S,Ts). % from a start state
accepts_from(S,[T|Ts]) :- % accepts the transitions when at S if...
delta(S,T,NextS), % there is a transition S->NextS via T
accepts_from(NextS,Ts). % and you can accept the remaining Ts from NextS.
accepts_from(S,[T|Ts]) :- % accepts the transitions when at S if...
\+ delta(S,T,NextS), % there is NO transition S->NextS via T
format(string(Txt),"No transition at ~q to reach ~q",[S,[T|Ts]]),
throw(Txt).
accepts_from(S,[]) :- % if there is no transition left, we accept if...
end(S). % we are a final state
And so:
?- accepts([d,a,b,a,b,b,b,c,d,c]).
true ; % yup, accepts but maybe there are other paths?
false. % nope
?- accepts([d,a,a,a,a,e]).
true ;
false.
?- accepts([d,a,a,a,a]).
false.
?- accepts([d,c,e,a]).
ERROR: Unhandled exception: "No transition at 3 to reach [e,a]"
The above code should also be able to find acceptable paths through the graph. But it does not:
?- accepts(T).
... infinite loop
This is not nice.
The primary reason for that is that accept/2 will immediately generate an infinite path looping at state 2 via transitions a and b. So one needs to add a "depth limiter" (the keyword is "iterative deepening").
The second reason would be that the test \+ delta(S,T,NextS) would succeed at node 4 for example (because there is nowhere to go from that node) and cause an exception before trying out the possibility of going nowhere (the last clause). So when generating, throwing is a hindrance, one just wants to reject.
Addendum: Also generate
The following only accepts/rejects and does not throw, but can also generate.
:- use_module(library(clpfd)).
accepts(Ts,L) :- % Accept the list of transitions Ts of length L if
start(S), % ...starting from a start state S
accepts_from(S,Ts,L). % ...you can accept the Ts of length L.
accepts_from(S,[T|Ts],L) :- % Accept the transitions [T|Ts] when at S if
(nonvar(L)
-> L >= 1
; true), % L (if it is bound) is at least 1 (this can be replaced by L #> 0)
delta(S,T,SN), % ...and there is a transition S->SN via T
Lm #= L-1, % ...and the new length is **constrained to be** 1 less than the previous length
accepts_from(SN,Ts,Lm). % ...and you can accept the remaining Ts of length Lm from SN.
accepts_from(S,[],0) :- % If there is no transition left, length L must be 0 and we accept if
end(S). % ...we are a final state.
delta(1,d,2).
delta(2,a,2).
delta(2,b,2).
delta(2,d,4).
delta(2,e,5).
delta(2,c,3).
delta(3,d,6).
delta(6,c,5).
start(1).
end(4).
end(5).
generate :-
between(0,7,L),
findall(Ts,accepts(Ts,L),Bag),
length(Bag,BagLength),
format("Found ~d paths of length ~d through the graph\n",[BagLength,L]),
maplist({L}/[Ts]>>format("~d : ~q\n",[L,Ts]),Bag).
And so:
?- accepts([d,a,b,a,b,b,b,c,d,c],_).
true ;
false.
?- accepts([d,a,a,a,a],_).
false.
?- accepts([d,c,e,a],_).
false.
?- generate.
Found 0 paths of length 0 through the graph
true ;
Found 0 paths of length 1 through the graph
true ;
Found 2 paths of length 2 through the graph
2 : [d,d]
2 : [d,e]
true ;
Found 4 paths of length 3 through the graph
3 : [d,a,d]
3 : [d,a,e]
3 : [d,b,d]
3 : [d,b,e]
true ;
Found 9 paths of length 4 through the graph
4 : [d,a,a,d]
4 : [d,a,a,e]
4 : [d,a,b,d]
4 : [d,a,b,e]
4 : [d,b,a,d]
4 : [d,b,a,e]
4 : [d,b,b,d]
4 : [d,b,b,e]
4 : [d,c,d,c]
true
Here's my answer. I sought to completely separate the data from the logic.
There are rules to infer the possible paths, start and end nodes.
The edge/2 predicate stands for either an alpha or a delta line.
The path (DCG) predicate describes a list of edges that ends with an end node.
The start and end nodes are inferred using the start_node/1 and end_node/1 predicates.
Finally, the phrase/3 is used to describe the list of paths that are valid automata.
delta(1, d, 2).
delta(2, d, 4).
delta(2, e, 5).
delta(2, c, 3).
delta(3, d, 6).
delta(6, c, 5).
alpha(2, a, 2).
alpha(2, b, 2).
edge(Node, Node, Via) :-
alpha(Node, Via, Node).
edge(From, To, Via) :-
delta(From, Via, To).
path(From, To) -->
{ end_node(To),
dif(From, To),
edge(From, To, Via)
},
[Via].
path(From, To) -->
{edge(From, Mid, Via)},
[Via],
path(Mid, To).
start_node(Node) :-
node_aux(start_node_aux, Node).
end_node(Node) :-
node_aux(end_node_aux, Node).
start_node_aux(Node) :-
edge(Node, _, _),
\+ edge(_, Node, _).
node_aux(Goal, Node) :-
setof(Node, call(Goal, Node), Nodes),
member(Node, Nodes).
end_node_aux(Node) :-
edge(_, Node, _),
\+ edge(Node, _, _).
automaton -->
{start_node(Start)},
path(Start, _End).
accept(Steps) :-
length(Steps, _N),
phrase(automaton, Steps).
I suspect that David did not use Definite Clause Grammars because you should be familiar with the basics before learning DCGs.
I have predicates of students and sports they do, and I want to find out which students do a particular sport. I have this sofar, but i can only get results if I enter exact sports in a list , and my find predicate works only to find a sport in a list. I don't know how to put it together to use to find students that do 1 sport:
student('Quinton Tarentino', male, 12).
student('Tom Hanks', male, 9).
student('Ed Harris', male, 11).
does_sport('Quinton Tarentino', [soccer, hockey, cricket]).
does_sport('Tom Hanks', []).
does_sport('Ed Harris', [hockey, swimming]).
sports([soccer, hockey, swimming, cricket, netball]).
find(X) :- sports(L), member(X, L).
I tried things like:
?- does_sport(X, find(soccer, L)).
This just returns false. I know I need to link my sports list to the does_sports predicate but not sure how.
Any advice appreciated :)
To find out which students do a particular sport, you could define a predicate like so:
student_sport(St,Sp) :-
does_sport(St,L), % L is a list of sports student St does
member(Sp,L). % Sp is a member of list L
Then you can query for e.g. soccer, as you seem to intend in your question, like so:
?- student_sport(St,soccer).
St = 'Quintin Tarentino' ? ;
no
Hockey on the other hand yields two results:
?- student_sport(St,hockey).
St = 'Quintin Tarentino' ? ;
St = 'Ed Harris' ? ;
no
If you want to have a list of students doing hockey instead, you can use findall/3 like so:
?- findall(St,student_sport(St,hockey),L).
L = ['Quintin Tarentino','Ed Harris']
Or alternatively setof/3 to get a sorted list (without duplicates, in case you happened to have facts that contain any):
?- setof(St,student_sport(St,hockey),L).
L = ['Ed Harris','Quintin Tarentino']
Note that in some Prologs you might have to explicitly include a library to use member/2, e.g. in Yap: :- use_module(library(lists))., while others autoload it, e.g. SWI.
EDIT:
Concerning the issues you raised in your comment, let's maybe start with your observation that student_sport/2 produces the answers one at a time. That is intentional, as suggested by the predicate name that contains the word student in singular: It describes a relation between a student and a particular sport that very student practices. That's why I added the example queries with findall/3 and setof/3, to show ways how you can collect solutions in a list. You can easily define a predicate students_sport/2 that describes a relation between a particular sport and a list of all students who practice it:
students_sport(L,Sp) :-
setof(St,student_sport(St,Sp),L).
Concerning the sports-austere, you can choose an atom to denote that case, say none and then add an according rule to student_sport/2 like so:
student_sport(St,none) :- % <- rule for the sports-austere
does_sport(St,[]). % <- succeeds if the student does no sport
student_sport(St,Sp) :-
does_sport(St,L),
member(Sp,L).
This yields the following results:
?- student_sport(St,none).
St = 'Tom Hanks' ? ;
no
?- students_sport(St,none).
St = ['Tom Hanks']
?- students_sport(St,hockey).
St = ['Ed Harris','Quintin Tarentino']
?- students_sport(St,Sp).
Sp = cricket,
St = ['Quintin Tarentino'] ? ;
Sp = hockey,
St = ['Ed Harris','Quintin Tarentino'] ? ;
Sp = none,
St = ['Tom Hanks'] ? ;
Sp = soccer,
St = ['Quintin Tarentino'] ? ;
Sp = swimming,
St = ['Ed Harris']
And finally, concerning your assumption of your code being exactly as I wrote it: There is a similarity in structure, namely your predicate find/1 having a first goal (sports/1) involving a list and subsequently using member/2 to check for membership in that list. The second rule (or single rule before the edit) of student_sport/2 is also having a first goal (but a different one: does_sport/2) involving a list and subsequently using member/2 to check for membership in that list. Here the similarities end. The version I provided is not using sports/1 at all but rather the list of sports associated with a particular student in does_sport/2. Note that find/1 does not describe any connection to students whatsoever. Furthermore your query ?- does_sport(X, find(soccer, L)). indicates that you seem to expect some sort of return value. You can regard predicates as functions returning true or false but that is usually not very helpful when programming Prolog. The argument find(soccer,L) is not being called as you seem to expect, but literally passed as an argument. And since your facts do not include something along the lines of
does_sport(*SomeStudentHere*, find(soccer,L)).
your query fails.
I am working with chemical compounds names.I used to DCG rules to define the name format.but the input is a list format,that not a good way.because the elements separate by separater.but i want as one string input like "1-butene" it is send as ['1','-',but,ene] .my code is here.
stem11-->[but]|[pent]|[hex]|[hept].
suf --> [ene]|[yne].
seperater-->['-'].
numerals-->['1']|['2']|['3']|['4']|['5']|['6']|['7'].
main-->numerals,seperater,stem11,suf.
check(S):-tokenize(S,L),main(L,[]).
here
tokenize("1-butene",L).
L=['1','-',but,ene].i want the tokenizing code the input may be like [1-butene].i tried many ways but couldn't get the proper code.please help me.
To keep things simple, I would do tokenization inline:
stem11 -->"but"|"pent"|"hex"|"hept".
suf -->"ene"|"yne".
seperator -->"-".
numerals -->"1"|"2". % etc
main -->numerals,seperator,stem11,suf.
then use ?- phrase(main, "1-butene"). or ?- main("1-butene", []).
edit
stem11(A) --> atom(["but","pent","hex","hept"], A).
suf(A) --> atom(["ene","yne"], A).
separator(-) -->"-".
numerals(A) --> atom(["1","2","3","4","5","6","7"], A).
atom(L, A) --> {member(S, L)}, atom_match(S), {atom_codes(A, S)}.
atom_match([]) --> [].
atom_match([C|Cs]) --> [C], atom_match(Cs).
tokenize([A,B,C,D]) --> numerals(A), separator(B), stem11(C), suf(D).
check(S,L) :- phrase(tokenize(L), S, []).
yields (tested with GnuProlog)
?- check("1-butene",L).
L = ['1',-,but,ene] ?
Let's try my answer,Here my input like"1-butene"
stem11-->[but]|[pent]|[hex]|[hept].
suf --> [ene]|[yne].
seperater-->['-'].
numerals-->['1']|['2']|['3']|['4']|['5']|['6']|['7'].
main-->numerals,seperater,stem11,suf.
:-set_prolog_flag(double_quotes, codes).
any(A,K) --> {member(S,K)}, S, {atom_codes(A, S)}.
words(A) --> any(A,["but","pent","hex","hept","ene","yne","-","1","2","3"]).
split([]) --> "".
split([X|Xs]) --> words(X), split(Xs).
tokenize(S,L):-phrase(split(L),S).
check(S):-tokenize(S,L),main(L,[]).
query like this.
?- tokenize("1-butene",L).
L = ['1', -, but, ene] ;
?- check("1-butene").
true ;
I have a predicate, which is true, if passed such list of pairs, for instance:
translatable([(dog,perro)], [(perro,hund)], [(dog,hund)])
Means - if "dog" translates to "perro", and "perro" translates to "hund", then it is true that "dog" translates to "hund".
Here follows full code. Returns/suggests first member of pair - given ((a, b), a) returns true, given ((a, b), X) returns X = a:
first((First, _), First).
Similar to "first", but for second pair member:
second((_, Second), Second).
This returns true if translatable word exists in list of tuples, and saves translation to Translation: (dog, Translation, [(bed,cama),(dog,perro)]
translation_exists(Word, Translation, [H|T]) :-
first(H, Word), second(H, Translation), !;
translation_exists(Word, Translation, T).
And resulting:
translatable(EnglishSpanish, SpanishGerman, EnglishGerman) :-
forall(member(Pair, EnglishGerman), (
first(Pair, Word),
second(Pair, ResultTranslation),
translation_exists(Word, Translation, EnglishSpanish),
translation_exists(Translation, ResultTranslation, SpanishGerman)
)).
This code returns true/false correctly.
But why, given
translatable([(dog,perro)], [(perro,hund)], X).
It does not returns X = [(dog,hund)]?
EDIT
To be more specific, actual goal is:
to find out if LAST dictionary has translatable pairs (and them only).
Daniel, thanks a lot, I have adopted your suggested member function - great simplification, thank you! This is all the code I have now:
lastIsTranslatable(_, _, []).
lastIsTranslatable(EngSpan, SpanGerm, [(Eng, Germ) | T]) :-
member((Eng, Span), EngSpan),
member((Span, Germ), SpanGerm),
% this is to protect endless [(dog,hund), (dog, hund), ...]
not(member((Eng, Germ), T)),
lastIsTranslatable(EngSpan, SpanGerm, T),
!.
And still, this works great finding True & False:
lastIsTranslatable([(a,b)], [(b,c)], [(a,c)]).
lastIsTranslatable([(a,b)], [(b,c)], [(a,no)]).
But for
lastIsTranslatable([(a,b)], [(b,c)], X).
result is X= [], then, after hitting ";" - false. Why?
Well, running with trace option, I see execution is failing on
not(member((Eng, Germ), T))
But otherwise resulting X will be endlessly filled with (a,c), (a,c)... Maybe there is better way to protect from duplicates?
The reason, basically, is that because EnglishGerman is uninstantiated, member/2 is free to come up with possible lists for it:
?- member((perro,X), List).
member((perro,X), List).
List = [ (perro, X)|_G18493911] ;
List = [_G18493910, (perro, X)|_G18493914] ;
List = [_G18493910, _G18493913, (perro, X)|_G18493917] ;
List = [_G18493910, _G18493913, _G18493916, (perro, X)|_G18493920]
...
This is the most direct issue, but even if you change the flow of data I think you'll still have problems:
translatable1(EnglishSpanish, SpanishGerman, EnglishGerman) :-
member((English,Spanish), EnglishSpanish),
member((Spanish,German), SpanishGerman),
member((English,German), EnglishGerman).
Note that I have foregone your first/2 and second/2 predicates in favor of pattern matching; I think this reads more clearly.
Aside: If you know your list is concrete and you don't want to generate multiple solutions, you can use memberchk/2 to verify that an element exists instead of member/2; it's cheaper and deterministic.
This works better (you get solutions, anyway) but still you get a lot more solutions than you need:
?- translatable1([(dog,perro)], [(perro,hund)], X).
X = [ (dog, hund)|_G18493925] ;
X = [_G18493924, (dog, hund)|_G18493928] ;
X = [_G18493924, _G18493927, (dog, hund)|_G18493931] a
Something which we know that our code does not know is that the cardinality of the result set should be less than or equal to the lowest cardinality of our inputs; if I have fifteen English-Spanish words and twelve Spanish-German words, I can't have more than twelve words in my English-German result. The reason our code doesn't know that is because it is trying to behave like math: our code is basically saying "for every element of English-Spanish, if there exists a matching element of Spanish-German, that is also an element of English-German." This does not tell us how to construct English-German! It only tells us a fact about English-German that we can verify with English-Spanish and Spanish-German! So it's cool, but it isn't quite enough to compute English-German.
Aside: it's conventional in Prolog to use a-b instead of (a,b); it's too easy to lull yourself into believing that Prolog has tuples when it doesn't and the operator precedence can get confusing.
So, how do we tell Prolog how to compute English-German? There are probably lots of ways but I would prefer to use select/3 because our set cardinality constraints (as well as a general sense that it will converge/halt) will emerge naturally from a computation that "uses up" the input sets as it goes.
translatable2([], _, []).
translatable2(_, [], []).
translatable2([Eng-Span|EngSpanRem], SpanGerm, EngGerm) :-
(select(Span-Germ, SpanGerm, SpanGermRem) ->
translatable2(EngSpanRem, SpanGermRem, EngGermRem),
EngGerm = [Eng-Germ|EngGermRem]
;
translatable2(EngSpanRem, SpanGerm, EngGerm)
).
The base cases should be obvious; if we are out of English-Spanish or Spanish-German, there's nothing left to compute. Then the inductive case peels the first item off the English-Spanish list and searches for a Spanish-German translation that matches. If it finds one, it uses it to build the result; otherwise, it just recurs on the remaining English-Spanish list. This way, on each iteration we at least discard an English-Spanish translation from that list, and we discard Spanish-German translations as they are used. So it seems intuitively likely that this will work and terminate without producing a bunch of extra choice points.
It seems to do the trick:
?- translatable2([dog-perro], [perro-hund], X).
X = [dog-hund] ;
X = [dog-hund].
The extra result there is because we hit both terminal cases because both lists became []; this isn't attractive but it isn't anything to worry about really either.
Now one thing that sucks about this solution is that it treats the first two parameters as in-parameters and the last one as an out-parameter and there isn't really anything you can do about this. I don't know if this is an issue for you; translatable/1 should not have this limitation, but because member((Spanish,German), SpanishGerman) happens before member((English,German), EnglishGerman) it winds up generating an infinitely large list, searching in effect for the missing Spanish-German translation.
Still, it feels like it should be possible to come up with a general purpose predicate that works as long as you supply any two of these inputs. I can do that if I know that all three lists are complete and in the same order:
translatable3([], [], []).
translatable3([X-Y|XYs], [Y-Z|YZs], [X-Z|XZs]) :-
translatable3(XYs, YZs, XZs).
And you can see it work like so:
?- translatable3([dog-perro], [perro-hund], X).
X = [dog-hund].
?- translatable3([dog-perro], X, [dog-hund]).
X = [perro-hund].
?- translatable3(X, [perro-hund], [dog-hund]).
X = [dog-perro].
But I don't know enough about your constraints to know if that could be a legitimate answer. My suspicion is no, because languages don't work that way, but who knows?
Anyway, that's three different approaches; I hope one of them is helpful to you!
I'm traying to learn the basics of logic programming.
I solved some exercises, and now I'm having trouble on creating a function that take two arguments, a list of non empty lists whose elements concatenated together form the second argument.
By the time I created a function that concat the elements of a list of lists:
concat([[]|L],L3):- concat(L,L3).
concat([[Head|L1]|L2],[Head|L3]):- concat([L1|L2],L3),!.
Now, what I need to know is how to take the return value of that function and compare it with a list (the second argument of the function).
This is one approach that will allow down to single elements in a sublist, but not an empty sublist:
concat([[L]], [L]).
concat([[H],L|T], [H|R]) :- concat([L|T], R).
concat([[H1,H2|T]|LT], [H1|RT]) :- concat([[H2|T]|LT], RT).
The method here to avoid an empty list is to call out two head elements in the recursive clause, and a solitary element list in the base case. This prevents empty sublists from succeeding, as requested in the comments of the original post.
If you have a variable, Y that is already instantiated, and you want to know if it is the result of concatenating the list of lists, LL, you simply query:
concat(LL, Y).
This will be true if Y is the concatenation of list LL and false if it is not. You don't have to "return and compare" (as, for example, in C, you might say, concat(LL) == Y, or concat(LL, X); if (X == Y)...). This is because concat is a relation defined between the two arguments and it determine if the query can be made true by following the stated rules (clauses of the predicate).
If you already obtained a result and want to determine if it's unifiable to another variable, Z, then you can say:
concat(X, Y), Y = Z.
Note that, in Prolog, concat(X, Y) == Z is not correct to determine if the result of the predicate is equal to Z because it is not a function that returns a value.
Prolog doesn't have functions or return values in the sense of a procedural programming language. It has predicates, which assert a relationship. It has terms, which include variables which comes with some strictures:
All variables are local, and
A variable, once assigned a value, ceases to be variable. That's why it's called unification.
So....
If you want a predicate that will take two lists and produce their concatenation, you'll need to pass it a 3rd variable. You might invoke it like this:
concat([a,b],[c,d],X).
which asserts that X is the concatenation of [a,b] and [c,d]. Prolog's inference engine will then evaluate the truth or falseness of the assertion.
Most recursive problems has a few special cases and a more general case. The implementation of such a concat/3 predicate might look something like this (annotated to explain what it's doing).
First, we have one special (and terminating) case: If the left-hand list is empty, the concatenation is simply the right-hand list.
concat( [] , Bs , Bs ).
Next, we have the one general case: if the left-hand list is non-empty, we need to prepend it to the concatentation that we're building (and then recurse down.)
concat( [A|As] , Bs , [A|Cs] ) :-
concat(As,Bs,Cs).
That's all there is two it. You'll also notice that it's bi-directional: it's perfectly happy to split lists apart as well. Invoking it like this:
concat( Prefix , Suffix, [a,b,c,d] ).
will, on backtracking, produce all the possible ways that [a,b,c,d] could be split into a prefix and suffix:
Prefix Suffix
--------- ---------
[] [a,b,c,d]
[a] [b,c,d]
[a,b] [c,d]
[a,b,c] [d]
[a,b,c,d] []
You just need the base case
concat([],[]).
concat([[]|L],L3):- concat(L,L3).
concat([[Head|L1]|L2],[Head|L3]):- concat([L1|L2],L3).
It also works for empty (sub)lists.
I've removed the useless cut.