Prolog : Divide word on syllables using predicate "Name" - prolog

I need to read a word in from the user and then split it into syllables
based on one of 2 rules: vowel-consonant-vowel, or
vowel-consonant-consonant-vowel.
Looks that predicate "name" does not work, should be word in output, not list
Could you please help?
vowel(a).
vowel(e).
vowel(i).
vowel(o).
vowel(u).
vowel(y).
consonant(L) :- not(vowel(L)).
ssplit(A,B) :- atom_chars(A,K),ssplit(K,B,-1). %convert atom to list
test(A,B) :- append(A,[],F), name(N,F).
ssplit([],[],0) :- append(L,[],F), name(N,F), writeln(N).
ssplit([H1|T1],[H1|T2],-1) :- ssplit(T1,T2,0).
ssplit([H1|T1],[H1|T2],0) :- consonant(H1), ssplit(T1,T2,0). %split to syllables
ssplit([H1|T1],[H1|T2],0) :- vowel(H1), ssplit(T1,T2,1).
ssplit([H1|T1],[H1|T2],1) :- vowel(H1), ssplit(T1,T2,1). %split to syllables
ssplit([H1|[]],[H1|T2],1) :- consonant(H1), ssplit([],T2,0).
ssplit([H1,H2|[]],[H1,H2|T2],1) :- consonant(H1), vowel(H2), ssplit([],T2,1).
ssplit([H1,H2|T1],['-',H1,H2|T2],1) :- consonant(H1), vowel(H2), ssplit(T1,T2,1).
ssplit([H1,H2|T1],T2,1) :- consonant(H1), consonant(H2), ssplit([H1,H2|T1],T2,2).
ssplit([H1,H2|[]],[H1,H2|T2],2) :- ssplit([],T2,0). %split to syllables
ssplit([H1,H2,H3|[]],[H1,H2,H3|T2],2) :- vowel(H3), ssplit([],T2,1).
ssplit([H1,H2,H3|T1],[H1,'-',H2,H3|T2],2) :- vowel(H3), ssplit(T1,T2,1).
ssplit([H1,H2,H3|T1],[H1,H2,H3|T2],2) :- consonant(H3), ssplit(T1,T2,0).
/*
ssplit(analog,L).
ssplit(ruler,L).
ssplit(prolog,L).
*/

DCG are more practical when handling input:
split_name(N, L) :-
atom_codes(N, Cs),
phrase(split_v(L), Cs, []).
split_v([]) --> [].
split_v([S|Syllables]) -->
vowel(X),
consonant(Y),
vowel(Z),
{atom_codes(S, [X,Y,Z])},
split_v(Syllables).
split_v([S|Syllables]) -->
vowel(V1),
consonant(C1),
consonant(C2),
vowel(V2),
{atom_codes(S, [V1,C1,C2,V2])},
split_v(Syllables).
% catch all unhandled
split_v([S|Syllables]) -->
[C], {atom_codes(S, [C])},
split_v(Syllables).
vowel(C) --> [C], {vowel(C)}.
consonant(C) --> [C], {\+vowel(C)}.
vowel(C) :- memberchk(C, "aeiou").
test:
?- split_name(stackoverflow,L).
L = [s, t, acko, v, e, r, f, l, o|...] ;
L = [s, t, a, c, k, ove, r, f, l|...] ;
L = [s, t, a, c, k, o, v, e, r|...] ;
false.

Related

Generate all words of length N and form a list with them in Prolog

Given the letters [a, b, c] generate the list containing all the words of length N, formed out of this letters.
For example:
?- generate(2, L).
should output:
L = [aa, ab, ac, ba, bb, bc, ca, cb, cc].
At first, this seemed like a pretty simple problem, but I've discovered that none of my implementations work.
This is the second implementation, the one that kind of works.
letter(X) :- member(X, [a, b, c]).
generateWord(0, []) :- !.
generateWord(N, [H|T]) :-
letter(H),
NextN is N - 1,
generateWord(NextN, T).
generateAtomicWord(N, Word) :-
generateWord(N, WList),
atomic_list_concat(WList, Word).
maxSolutions(N, R) :- R is N ** 3.
generate(N, CurrentList, ResultList) :-
maxSolutions(N, R),
length(CurrentList, L),
L =:= R,
append(CurrentList, [], ResultList), !.
generate(N, CurrentList, ResultList) :-
generateAtomicWord(N, NewWord),
\+ member(NewWord, CurrentList),
append(CurrentList, [NewWord], NewList),
generate(N, NewList, ResultList).
generate(N, ResultList) :-
generate(N, [], ResultList).
It kind of works because when given N = 3 the program outputs:
L = [aaa, aab, aac, aba, abb, abc, aca, acb, acc|...]
My first implementation is different, but I can't make it work on any case.
letter(X) :- member(X, [a, b, c]).
generateWord(0, []) :- !.
generateWord(N, [H|T]) :-
letter(H),
NextN is N - 1,
generateWord(NextN, T), !.
generateAtomicWord(N, Word) :-
generateWord(N, WList),
atomic_list_concat(WList, Word).
maxSolutions(N, R) :- R is N ** 3.
generate(N, [H]) :- generateAtomicWord(N, H).
generate(N, [H|T]) :-
generate(N, T),
length(T, TailLen),
maxSolutions(N, M),
(TailLen =:= M -> !;
generateAtomicWord(N, H),
\+ member(H, T)).
This one just outputs:
L = [aa]
and when requested for the rest of the solutions it cycles.
The problem must be solved without using predicates such as:
findall, findnsol, bagof, setof, etc...
that find all the solutions.
I've added the tag backtracking because it does resemble a backtracking problem, but I've no idea what a standard implementation might look like in Prolog.
It kind of works because when given N = 3 the program outputs:
L = [aaa, aab, aac, aba, abb, abc, aca, acb, acc|...]
That is not an error, that is the Prolog interpreter that displays the list in a shorter way. If you hit w when it shows the output, it will show the full list. For more information see this answer.
That being said, you make it too hard. You can first make a predicate that will unify a variable with all possible atoms:
letter(X) :- member(X, [a, b, c]).
word(0, []).
word(N, [C|W]) :-
N > 0,
N1 is N-1,
letter(C),
word(N1, W).
Now we can generate all possibilities with findall/3 [swi-doc], and use for example maplist/3 [swi-doc] with atomic_list_concat/2 to convert the list to a single atom:
words(N, L) :-
findall(W, word(N, W), Ws),
maplist(atomic_list_concat, Ws, L).
For example:
?- words(0, L).
L = [''].
?- words(1, L).
L = [a, b, c].
?- words(2, L).
L = [aa, ab, ac, ba, bb, bc, ca, cb, cc].
?- words(3, L).
L = [aaa, aab, aac, aba, abb, abc, aca, acb, acc|...].
We can generate a list of lists ourselves by updating a "difference" list until all possible words are generated:
wordlist(N, L) :-
wordlist(N, [], L, []).
wordlist(0, R, [W|T], T) :-
reverse(R, W),
!.
wordlist(N, C, L, T) :-
N > 0,
N1 is N-1,
wordfold([a,b,c], N1, C, L, T).
wordfold([], _, _, L, L).
wordfold([C|CS], N1, CT, L, T) :-
wordlist(N1, [C|CT], L, L2),
wordfold(CS, N1, CT, L2, T).
For example:
?- wordlist(0, L).
L = [[]].
?- wordlist(1, L).
L = [[a], [b], [c]].
?- wordlist(2, L).
L = [[a, a], [a, b], [a, c], [b, a], [b, b], [b, c], [c, a], [c|...], [...|...]].
You then still need to perform atomic_list_concat on it. I leave that as an exercise.

How can I reverse list and replace all the specified values with other value?

Imagine that we have a list: [1,2,2,3,4]. The problem can be splitted into two parts:
Reverse the list (we should receive [4,3,2,2,1])
Replace, e.g. all 2 with 100, so we should receive [4,3,100,100,1]
It's easy to reverse the list, here's a working code:
simple_reverse(List, Rev) :-
simple_reverse(List, Rev, []).
simple_reverse([], L, L).
simple_reverse([H|T], L, SoFar) :-
simple_reverse(T, L, [H|SoFar]).
But I have some troubles with replacing the elements. I have tried the following approach:
reverse(a, b, List, Rev) :-
reverse(a, b, List, Rev, []).
reverse(a, b, [], L, L).
reverse(a, b, [H|T], L, SoFar) :-
reverse(a, b, T, L, [H|SoFar]).
reverse(a, b, [a|T], L, SoFar) :-
reverse(a, b, T, L, [b|SoFar]).
What is the problem? By the way, I'm using https://swish.swi-prolog.org/ to run the code.
Instead of just a,b (which are atoms) you need to place variables in reverse/4 and reverse/5 predicates:
reverse(A, B, List, Rev) :-
reverse(A, B, List, Rev, []).
reverse(_, _, [], L, L).
reverse(A, B, [H|T], L, SoFar) :-
dif(A,H), %case where H is not A so we skip it
reverse(A, B, T, L, [H|SoFar]).
reverse(A, B, [A|T], L, SoFar) :-
reverse(A, B, T, L, [B|SoFar]).
You can use foldl in conjonction with library(lambda) :
:- use_module(library(lambda)).
my_reverse(L, A, B, R) :-
foldl([A,B] +\X^Y^Z^(dif(X, A) -> Z = [X |Y]; Z = [B|Y]), L, [],R).
example :
?- my_reverse([1,2,2,3,4], 2, 100, R).
R = [4, 3, 100, 100, 1].

word processing prolog

I am trying to break a word into different syllables in Prolog according to 2 different rules ..
rule 1: vowel-consonant-vowel (break word after second vowel)
rule 2: vowel-consonant-consonant-vowel (break word between the 2
consonant) , for example, calculator = cal-cula-tor ..
I already have the following code in Prolog, however, it only analyzes the first 3 or 4 letters of the word ..
I need it to process and analyze the entire word.
vowel(a).
vowel(e).
vowel(i).
vowel(o).
vowel(u).
consonant(L):- not(vowel(L)).
syllable(W, S, RW):-
atom_chars(W, [V1, C, V2|Tail]),
vowel(V1),
consonant(C),
vowel(V2),
!,
atomic_list_concat([V1, C, V2], S),
atomic_list_concat(Tail, RW).
syllable(W, S, RW):-
atom_chars(W, [V1, C, C2, V2|Tail]),
vowel(V1),
consonant(C),
consonant(C2),
vowel(V2),
!,
atomic_list_concat([V1, C, C2, V2], S),
atomic_list_concat(Tail, RW).
syllable(W, W, _).
break(W, B):-
syllable(W, B, ''), !.
break(W, B):-
syllable(W, S, RW),
break(RW, B2),
atomic_list_concat([S, '-', B2], B).
First, a setting that makes it much more convenient to specify lists of characters, and which I recommend you use in your code if you process text a lot:
:- set_prolog_flag(double_quotes, chars).
Second, the data, represented in such a way that the definitions can be used in all directions:
vowel(a). vowel(e). vowel(i). vowel(o). vowel(u).
consonant(C) :- maplist(dif(C), [a,e,i,o,u]).
For example:
?- consonant(C).
dif(C, u),
dif(C, o),
dif(C, i),
dif(C, e),
dif(C, a).
whereas the version you posted incorrectly says that there is no consonant:
?- consonant(C).
false.
The rules you outline are readily described in Prolog:
% rule 1: vowel-consonant-vowel (break after second vowel)
rule([V1,C,V2|Rest], Bs0, Bs, Rest) :-
vowel(V1), consonant(C), vowel(V2),
reverse([V2,C,V1|Bs0], Bs).
% rule 2: vowel-consonant-consonant-vowel (break between the consonants)
rule([V1,C1,C2,V2|Rest], Bs0, Bs, [C2,V2|Rest]) :-
vowel(V1), consonant(C1), consonant(C2), vowel(V2),
reverse([C1,V1|Bs0], Bs).
% alternative: no break at this position
rule([L|Ls], Bs0, Bs, Rest) :-
rule(Ls, [L|Bs0], Bs, Rest).
Exercise: Why am I writing [V2,C,V1|_] instead of [V1,C,V2|...] in the call of reverse/2?
Now, it only remains to describe the list of resulting syllables. This is easy with dcg notation:
word_breaks([]) --> [].
word_breaks([L|Ls]) --> [Bs],
{ rule([L|Ls], [], Bs, Rest) },
word_breaks(Rest).
word_breaks([L|Ls]) --> [[L|Ls]].
Now the point: Since this program is completely pure and does not incorrectly commit prematurely, we can use it to show that there are also other admissible hyphenations:
?- phrase(word_breaks("calculator"), Hs).
Hs = [[c, a, l], [c, u, l, a], [t, o, r]] ;
Hs = [[c, a, l], [c, u, l, a, t, o], [r]] ;
Hs = [[c, a, l], [c, u, l, a, t, o, r]] ;
Hs = [[c, a, l, c, u, l, a], [t, o, r]] ;
Hs = [[c, a, l, c, u, l, a, t, o], [r]] ;
Hs = [[c, a, l, c, u, l, a, t, o, r]].
In Prolog, it is good practice to retain the generality of your code so that you can readily observe alternative solutions. See logical-purity.
I guess its time for a DCG push back solution. The push back is used in the second rule of break//1. It is to reflect that we look at four characters but only consume two characters:
vowel(a). vowel(e). vowel(i). vowel(o). vowel(u).
consonant(C) :- \+ vowel(C).
break([V1,C,V2]) -->
[V1,C,V2],
{vowel(V1), consonant(C), vowel(V2)}.
break([V1,C1]), [C2,V2] -->
[V1,C1,C2,V2],
{vowel(V1), consonant(C1), consonant(C2), vowel(V2)}.
syllables([L|R]) --> break(L), !, syllables(R).
syllables([[C|L]|R]) --> [C], syllables([L|R]).
syllables([[]]) --> [].
So the overall solution doesn't need some extra predicates such as append/3 or reverse/2. We have also placed a cut to prune the search, which can be done because of the character catchall in the second rule of syllables//1.
Here are some example runs:
Jekejeke Prolog 2, Laufzeitbibliothek 1.1.6
(c) 1985-2016, XLOG Technologies GmbH, Schweiz
?- set_prolog_flag(double_quotes, chars).
Ja
?- phrase(syllables(R), "calculator").
R = [[c,a,l],[c,u,l,a],[t,o,r]] ;
Nein
?- phrase(syllables(R), "kitchensink").
R = [[k,i,t,c,h,e,n],[s,i,n,k]] ;
Nein
P.S.: In some older draft standards this DCG technique was
called "right-hand-context", and instead of the verb "push
back", the verb "prefixing" was used. In a newer draft standard
this is called "semicontext", and instead of the verb "push back",
the verb "restoring" is used.
https://www.complang.tuwien.ac.at/ulrich/iso-prolog/dcgs/dcgsdraft-2015-11-10.pdf
I think you could write it more simply.Here is my implementation:
syllable( Input, Final_Word):-
atom_chars( Input, Char_list),
(split(Char_list, Word)-> atom_chars( Final_Word, Word);
Final_Word=Input).
split([],[]).
split([X,Y,Z|T],[X,Y,Z,'-'|T1]):-
vowel(X),vowel(Z),
atom_chars( Input, T),
syllable(Input,T2),
atom_chars( T2, T1).
split([X,Y,Z,W|T],[X,Y,'-',Z|T1]):-
vowel(X),\+vowel(Y),\+vowel(Z),vowel(W),
atom_chars( Input, [W|T]),
syllable(Input,T2),
atom_chars( T2, T1).
split([X|T],[X|T1]):- \+vowel(X),split(T,T1).
split/2 splits the word adding '-' where it could be added following the above rules you stated and returns a list to syllable. atom_chars/2 transforms the list to a word. If the word couldn't be split then the output is the input.
Example:
?- syllable(calculator,L).
L = 'calcu-lato-r'.
I'm don't understand why you wrote 'calculator = cal-cula-tor ' since it doesn't follows the rules stated, since "cal" is not vowel-constant-vowel but constant-vowel-constant and same for the rest of thr word...

On mixing Prolog coroutining (freeze/2, when/2) and DCG

In my previous answer to the recent question "Prolog binary search tree test - unwanted parents' parent node comparison", I proposed mixing lazy_chain/2 which uses prolog-coroutining ...
:- use_module(library(clpfd)).
lazy_chain(Zs, R_2) :-
( var(R_2) -> instantiation_error(R_2)
; clpfd:chain_relation(R_2) -> freeze(Zs, lazy_chain_aux(Zs,R_2))
; otherwise -> domain_error(chain_relation, R_2)
).
lazy_chain_aux([], _).
lazy_chain_aux([Z0|Zs], R_2) :-
freeze(Zs, lazy_chain_aux_(Zs,R_2,Z0)).
lazy_chain_aux_([], _, _).
lazy_chain_aux_([Z1|Zs], R_2, Z0) :-
call(R_2, Z0, Z1),
freeze(Zs, lazy_chain_aux_(Zs,R_2,Z1)).
... together with dcg in_order//1 ...
in_order(nil) --> [].
in_order(node(X,L,R)) --> in_order(L), [X], in_order(R).
... like so:
?- lazy_chain(Zs, #<),
phrase(in_order(node(1,nil,nil)), Zs).
Zs = [1,23].
Is there a easy way to "push" lazy_chain into phrase/3 so that its scope is limited to the part of the sequence described by in_order//1?
Right now, I get ...
?- lazy_chain(Zs, #<),
phrase(in_order(node(1,nil,nil)), Zs0,Zs).
Zs0 = [1|Zs], freeze(Zs, lazy_chain_aux(Zs,#<)).
... which (of course) can fail upon further instantiation of Zs:
?- lazy_chain(Zs, #<),
phrase(in_order(node(1,nil,nil)), Zs0,Zs),
Zs = [3,2,1].
false.
How can I work around that and constrain lazy_chain to the part of the list-difference?
In the meantime I came up with the following hack:
lazy_chain_upto(R_2, P_2, Xs0, Xs) :-
( var(R_2) -> instantiation_error(R_2)
; clpfd:chain_relation(R_2) -> when((nonvar(Xs0) ; ?=(Xs0,Xs)),
lazy_chain_upto_aux(Xs0,Xs,R_2)),
phrase(P_2, Xs0, Xs)
; otherwise -> domain_error(chain_relation, R_2)
).
lazy_chain_upto_aux(Xs0, Xs, _) :-
Xs0 == Xs,
!.
lazy_chain_upto_aux([], _, _).
lazy_chain_upto_aux([X|Xs0], Xs, R_2) :-
when((nonvar(Xs0) ; ?=(Xs0,Xs)), lazy_chain_upto_prev_aux(Xs0,Xs,R_2,X)).
lazy_chain_upto_prev_aux(Xs0, Xs, _, _) :-
Xs0 == Xs,
!.
lazy_chain_upto_prev_aux([], _, _, _).
lazy_chain_upto_prev_aux([B|Xs0], Xs, R_2, A) :-
call(R_2, A, B),
when((nonvar(Xs0) ; ?=(Xs0,Xs)), lazy_chain_upto_prev_aux(Xs0,Xs,R_2,B)).
Based on this we could define in_orderX//1 like this:
in_orderX(T) --> lazy_chain_upto(#<, in_order(T)).
The sample query shown in the question ...
?- phrase(in_orderX(node(1,nil,nil)), Zs0,Zs), Zs = [3,2,1].
Zs0 = [1,3,2,1], Zs = [3,2,1].
... now checks out alright, but still I wonder: is it worth it?
I don't see any problem mixing corouting and DCG. DCG is only a translation from the DCG rules H --> B, to some ordinary Prolog rules H' :- B'. Any constraint posting can be wrapped into {}/1.
Here is an example from Quines:
% eval(+Term, +List, -Term, +Integer)
eval([quote,X], _, X) --> [].
eval([cons,X,Y], E, [A|B]) -->
step,
eval(X, E, A),
eval(Y, E, B).
eval([lambda,X,B], E, [closure,X,B,E]) --> [].
eval([X,Y], E, R) -->
step,
{neq(X, quote), sto(B)},
eval(X, E, [closure,Z,B,F]),
{sto(A)},
eval(Y, E, A),
eval(B, [Z-A|F], R).
eval(S, E, R) -->
{freeze(S, is_symbol(S)), freeze(E, lookup(S, E, R))}.
You could do the same for lazy_chain_upto//2. As a start you
could go on an define the first clause of lazy_chain_upto//2
as follows:
lazy_chain_upto(R_2, P_2) -->
( {var(R_2)} -> {instantiation_error(R_2)}
; {clpfd:chain_relation(R_2)} -> /* ?? */
; {otherwise} -> {domain_error(chain_relation, R_2)}
)
In the /* ?? */ part you could profit from a DCG-ifyed lazy_chain_upto_aux//1 predicate as well. Of course I am assuming that the DCG translation understands (->) and (;)/2.
Bye

prolog general rule for finding cousins etc

The question is to write a general rule to find any level of relative!
cousin(N,Child1,Child2).
So that it is true if Child1 and Child2 are Nth cousins. So
cousin1(Child1,Child2) = cousin(1,Child1,Child2) and
cousin2(Child1,Child2) = cousin(2,Child1,Child2) and so on
for third and fourth and even higher level cousins.
What I have so far:
/* first person is parent of second person */
parent(a, b).
parent(b, f).
parent(a, d).
parent(f, g).
parent(a, k).
parent(f, h).
parent(k, l).
parent(f, i).
parent(k, m).
parent(l, t).
parent(b, e).
sibling(X,Y) :- parent(Z,X), parent(Z,Y), not(X=Y).
grandparent(X, Z) :-
parent(X, Y),
parent(Y, Z).
greatgrandparent(X, Z) :-
parent(X, Y),
parent(Y, P),
parent(P, Z).
cousin1(Child1,Child2) :-
parent(Y1,Child1),
parent(Y2,Child2),
sibling(Y1,Y2).
cousin2(Child1,Child2) :-
greatgrandparent(Z, Child1),
greatgrandparent(Z, Child2),
\+sibling(Child1, Child2),
\+cousin1(Child1, Child2),
Child1 \= Child2.
This returns false regardless of values input, so clearly I have no idea what I am doing PLEASE help!
cousin(N,Child1,Child2) :-
nth0(N, parent(Y1,Child1),Y1),
nth0(N, parent(Y2,Child2),Y2),
cousin1(Y1,Y2).
I tried:
% first person is parent of second person
parent(a, b).
parent(b, f).
parent(a, d).
parent(f, g).
parent(a, k).
parent(f, h).
parent(k, l).
parent(f, i).
parent(k, m).
parent(l, t).
parent(b, e).
sibling(Sib1,Sib2) :- parent(SomeParent,Sib1),
parent(SomeParent,Sib2),
\+ Sib1 = Sib2.
% first person is ancestor of second person
ancestor(Older,Younger,L) :-
parent(Older,Younger),
L is 1.
ancestor(Older,Younger,Level) :-
parent(Older,Child),
ancestor(Child,Younger,L),
Level is L + 1.
%nth_cousin(Level,Cous1,Cous2) :-
% ancestor(Sib1,Cous1,Level),
% ancestor(Sib2,Cous2,Level),
% sibling(Sib1,Sib2).
nth_cousin(Level,Cous1,Cous2) :-
setof((Cous1,Cous2), Sib1^Sib2^(ancestor(Sib1,Cous1,Level),
ancestor(Sib2,Cous2,Level),
sibling(Sib1,Sib2)
),
Cousins),
member((Cous1,Cous2), Cousins),
\+ (Cous2#<Cous1, member((Cous2,Cous1), Cousins)).
Ex.
1 ?- nth_cousin(1,Cous1,Cous2).
Cous1 = e,
Cous2 = l ;
Cous1 = e,
Cous2 = m ;
Cous1 = f,
Cous2 = l ;
Cous1 = f,
Cous2 = m ;
false.
2 ?- nth_cousin(2,Cous1,Cous2).
Cous1 = g,
Cous2 = t ;
Cous1 = h,
Cous2 = t ;
Cous1 = i,
Cous2 = t ;
false.

Resources