I'm trying to implement Levenshtein distance in Prolog.
The implementation is pretty straightforward:
levenshtein(W1, W2, D) :-
atom_length(W1, L1),
atom_length(W2, L2),
lev(W1, W2, L1, L2, D),
!.
lev(_, _, L1, 0, D) :- D is L1, !.
lev(_, _, 0, L2, D) :- D is L2, !.
lev(W1, W2, L1, L2, D) :-
lev(W1, W2, L1 - 1, L2, D1),
lev(W1, W2, L1, L2 - 1, D2),
lev(W1, W2, L1 - 1, L2 - 1, D3),
charAt(W1, L1, C1),
charAt(W2, L2, C2),
( C1 = C2 -> T is 0; T is 1 ),
min(D1, D2, D3 + T, D).
% Returns the character at position N in the atom A
% The position is 1-based
% A: The atom
% N: The position at which to extract the character
% C: The character of A at position N
charAt(A, N, C) :- P is N - 1, sub_atom(A, P, 1, _, C).
% min(...): These rules compute the minimum of the given integer values
% I1, I2, I3: Integer values
% M: The minimum over the values
min(I1, I2, M) :- integer(I1), integer(I2), ( I1 =< I2 -> M is I1; M is I2).
min(I1, I2, I3, M) :- min(I1, I2, A), min(I2, I3, B), min(A, B, M).
However, this code failures with this error:
?- levenshtein("poka", "po", X).
ERROR: Out of local stack
I'm using SWIPL implementation on Mac OS X Sierra.
There is a good reason for which your program does not work: your recursive calls lead into an infinite loop.
This is caused by those lines:
lev(W1, W2, L1 - 1, L2, D1),
lev(W1, W2, L1, L2 - 1, D2),
lev(W1, W2, L1 - 1, L2 - 1, D3),
min(D1, D2, D3 + T, D)
In Prolog things like L1 - 1 are expressions that do not get evaluated to numbers. Therefore your code will recursively call lev with the third argument as L1 -1, then L1 - 1 - 1, etc. which does not match your terminating rules.
To fix this you need to use temporary variables where you evaluate the result of e.g. L1 - 1.
This fixes it:
lev(W1, W2, L1, L2, D) :-
L11 is L1 - 1,
L22 is L2 - 1,
lev(W1, W2, L11, L2, D1),
lev(W1, W2, L1, L22, D2),
lev(W1, W2, L11, L22, D3),
charAt(W1, L1, C1),
charAt(W2, L2, C2),
( C1 = C2 -> T is 0; T is 1 ),
D4 is D3 + T,
min(D1, D2, D4, D).
Now this does this:
?- levenshtein("poka","po",X).
X = 0.
Which is probably not the result you want, but at least it does not error. I will leave it to you to fix your predicate.
There are several problems with your program.
The loop
#Fatalize already gave you a reason, here is a general method how you can localize such problems, using a failure-slice by which some goals false are inserted into your program. If the remaining program loops, also the original version did:
?- levenshtein("poka","po",X), false.
levenshtein(W1, W2, D) :-
atom_length(W1, L1),
atom_length(W2, L2),
lev(W1, W2, L1, L2, D), false,
!.
lev(_, _, L1, 0, D) :- D is L1, !.
lev(_, _, 0, L2, D) :- D is L2, !.
lev(W1, W2, L1, L2, D) :-
lev(W1, W2, L1 - 1, L2, D1), false,
lev(W1, W2, L1, L2 - 1, D2),
lev(W1, W2, L1 - 1, L2 - 1, D3),
charAt(W1, L1, C1),
charAt(W2, L2, C2),
( C1 = C2 -> T is 0; T is 1 ),
min(D1, D2, D3 + T, D).
You have to modify something in the remaining, visible part. Otherwise, this problem will persist.
Use lists!
Instead of using atoms or strings, better use lists to represent words. The best is to add into your .swiplrc or .sicstusrc:
:- set_prolog_flag(double_quotes, chars).
In this manner, the following holds:
?- "abc" = [a,b,c].
Avoid cuts
Cuts somehow, sometimes work, but such programs are hard-to-debug. In particular for beginners. Therefore, avoid them at all costs
Use clean arithmetics
You are using the "olde" arithmetic of Prolog which is highly moded. Instead use_module(library(clpfd)) to get purer code.
Related
I need to write the predicate Frequest(InList, OutList) to find the list
OutList of all elements that occur most frequently in the given InList.
Here is my code, help me write more professional and understandable for everyone please.
`counter([], _, 0).
counter([X|T], X, C) :- counter(T, X, C1), C is C1 + 1.
counter([X|T], Y, C) :- X == Y, counter(T, Y, C).
max_count([], , 0).
max_count([E|L], L1, C):-
counter(L1, E, C1),
maxcount(L, L1, C2),
C is max(C1, C2), !.
max_count_el([], , _, []) :- !.
max_count_el([X|L], L1, M, LR) :-
ffff(L, L1, M, LR2),
( counter(L1, X, C),
C == M,
+ member(X, LR2),
append(LR2, [X], LR);
LR = LR2
).
frequentest(L1, L2):-
max_count(L1, L1, R),
max_count_el(L1, L1, R, L2), !.`
My problem is:
Write a Prolog program that, given two lists L1 and L2
, outputs
two new lists L3 and L4
such that L3 contains the elements of
L1 which also belong to L2
, while L4 contains the elements
of L1 which do not belong to L2
. You may use the built-in
predicate member.
As an example, the query listmem([a, r, t], [t, s, m, n, a], L3, L4)
produces L3 = [a, t] and L4 = [r].
And my solution is now:
memberOf([], [], _).
memberOf([H|T], L2, [X|Xs]) :-
X is H,
member(X, L2),
memberOf(T, L2, Xs).
But when I test on the following input, it returns no..
memberOf( [1,2,3], [2,3,4], L3 ).
What am I doing wrong?
listmem(L1,[],[],L1).
listmem([],_,[],[]).
listmem([H1|T1],L2,[H1|L3],L4):-
member(H1,L2), !,
listmem(T1,L2,L3,L4).
listmem([H1|T1],L2,L3,[H1|L4]):-
listmem(T1,L2,L3,L4).
I've added a cut to the third case instead of a \+ member(H1,L2) to the last one so you can use listmem the "other way around", e.g., instead of:
?- listmem([a, r, t], [t, s, m, n, a], L3, L4).
L3 = [a, t],
L4 = [r].
You can also:
?- listmem([a, r, t], L2, [a,t], L4).
L2 = [a, t|_G10554000],
L4 = [r].
I want to split a list into 2 lists at a pivot P, if the number is less than P it goes into L1 and if it is greater than P then it will go into L2.
This is what I have so far, I am able to split a list L into L1 = [[],[]] in this form. But I want to split the list into 2 lists L1, and L2, how would I do that?
split(L,P,L1):-
split(L,P,[],L1).
split([],_,[],[]).
split([],_,X,[X]) :- X \= [].
split([P|T],P,[],L1) :- split(T,P,[],L1).
split([P|T],P,L,[L|L1]) :- L \= [], split(T,P,[],L1).
split([H|T],P,S,L1) :- H \= P, append(S, [H], S2), split(T,P,S2,L1).
You only need three rules to implement this predicate:
:- use_module(library(clpfd)).
split([], _, [], []).
split([H|T], P, L1, [H|T2]) :-
H #>= P,
split(T, P, L1, T2).
split([H|T], P, [H|T1], L2) :-
H #< P,
split(T, P, T1, L2).
The code is fairly straightforward
Note that, because of library(clpfd), this predicate also works e.g. when the initial list and the pivot are not known:
?- split(L,P,[5,47],[101]).
L = [101, 5, 47],
P in 48..101 ;
L = [5, 101, 47],
P in 48..101 ;
L = [5, 47, 101],
P in 48..101 ;
false.
Using partition/4
As mentionned in a comment, you can use partition/4 to do this:
split(L, P, L1, L2) :-
partition(zcompare(>,P), L, L1, L2).
However, this will not exhibit as many different behaviours as the first implementation.
i want to get a sublist of all elements that assemble specific word for example
for the call
assemble([hello,'',world,hi,bye,good,well], 'hello world', A).
program should print
A=[hello,'',world]
for the call
assemble([abc,123,ab,c,123],'abc123', A).
program should print
A=[abc,123];
A=[ab,c,123];
thanks for your help.
matchwords(W1, W2, Results) :-
setof(R, matchw(W1, W2, R), RSet), % Collect all the matching substrings
% and their lengths
reverse(RSet, Set), % Order by longest first
highest(Set, Results). % keep only the highest ones
matchw(W1, W2, N-Result) :-
atom_chars(W1, A1),
atom_chars(W2, A2),
matchl(A1, A2, R),
length(R, N),
atom_chars(Result, R).
matchl([H|T1], [H|T2], [H|T]) :-
matchl(T1, T2, T).
matchl([H1|T1], [H2|T2], R) :-
H1 \= H2,
( matchl(T1, [H2|T2], R) ; matchl([H1|T1], T2, R) ).
matchl([], _, []).
matchl([_|_], [], []).
highest([_-W], [W]).
highest([N1-W1,N2-_|_], [W1]) :-
N1 > N2.
highest([N1-W1,N2-W2|T], [W1|WT]) :-
N1 = N2,
highest([N2-W2|T], WT).
So I am working on a practice problem, where I need to find the number of cells with opening right, up , down and left. I have a working solution but I want to change it into clauses. I don't want to to use -> to define if and else. how can I fix the code below without the affecting the solution.
Here is the code:
stats(U,D,L,R) :- maze(Size,_,_,_,_),
findall(R, genXY(Size,R), Out),
statsHelp(Out,U, L, R, D).
statsHelp([],0,0,0,0).
statsHelp([[X|[Y]]|Tl],U, L, R, D) :- cell(X,Y,Dirs,Wt),
(contains1(u,Dirs) -> U1 is 1; U1 is 0), % how do i remove -> and separate them into clauses?
(contains1(d,Dirs) -> D1 is 1; D1 is 0),
(contains1(l,Dirs) -> L1 is 1; L1 is 0),
(contains1(r,Dirs) -> R1 is 1; R1 is 0),
statsHelp(Tl,U2, L2, R2, D2),
U is U1 + U2,
D is D1 + D2,
R is R1 + R2,
L is L1 + L2.
contains1(V,[V|Tl]).
contains1(V,[Hd|Tl]):-
contains1(V,Tl).
Note that your contains1/2 predicate is just the standard member/2, which you could use instead.
As for a version that always succeeds and produces an extra 0 or 1, I think you have to use a cut or negation to achieve that. Here is one with negation:
member_reified(X, Xs, Result) :-
member(X, Xs),
Result = 1.
member_reified(X, Xs, Result) :-
\+ member(X, Xs),
Result = 0.
You could of course also just move your use of -> into your definition of member_reified/3. Different implementations can give different trade-offs on backtracking; this one may succeed several times:
?- member_reified(x, [a, b, c], Result).
Result = 0.
?- member_reified(a, [a, b, c], Result).
Result = 1 ;
false.
?- member_reified(a, [a, b, c, a], Result).
Result = 1 ;
Result = 1 ;
false.