Prolog从本地堆栈中提升是没有充分理由的

Prolog从本地堆栈中提升是没有充分理由的,prolog,levenshtein-distance,failure-slice,non-termination,Prolog,Levenshtein Distance,Failure Slice,Non Termination,我正在尝试用Prolog实现 实现非常简单: 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 -

我正在尝试用Prolog实现

实现非常简单:

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).

我正在Mac OS X Sierra上使用SWIPL实现。

有一个很好的理由说明您的程序无法工作:递归调用导致无限循环

这是由以下几行引起的:

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)
在Prolog中,像L1-1这样的表达式不会计算为数字。因此,您的代码将递归调用lev,第三个参数为L1-1,然后是L1-1-1,等等,这与您的终止规则不匹配

要解决这个问题,您需要使用临时变量来评估结果,例如L1-1

这就解决了这个问题:

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).
这可能不是您想要的结果,但至少不会出错。我将留给您来修复谓词。

您的程序有几个问题

环路 @Fatalize已经给了您一个理由,下面是一个通用方法,您可以使用false将一些目标插入到您的程序中,从而将这些问题本地化。如果其余程序循环,原始版本也会:

?- 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). 以这种方式,以下内容适用:

?- "abc" = [a,b,c].
避免割伤 某种程度上,削减有时是可行的,但这样的程序很难调试。特别是对于初学者。因此,要不惜一切代价避免它们

使用干净的算术
您使用的是高度现代化的Prolog的旧算法。相反,使用_modulelibraryclpfd来获得更纯净的代码。

我对Prolog一无所知,但您的代码显然是递归的,而且递归可能会变得无界,并破坏堆栈。我会仔细检查终止条件。我自己通过在线调试器找到它,但这对未来的搜索者很有用 ?- 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).
:- set_prolog_flag(double_quotes, chars).
?- "abc" = [a,b,c].