繁体   English   中英

Prolog毫无理由地退出本地堆栈

[英]Prolog raises out of local stack for no good reason

我正在尝试在Prolog中实现Levenshtein距离

实现非常简单:

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

但是,此代码因以下错误而失败:

?- levenshtein("poka", "po", X).
ERROR: Out of local stack

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

现在这样做:

?- levenshtein("poka","po",X).
X = 0.

这可能不是您想要的结果,但至少不会出错。 我将把它交给您来修复您的谓词。

您的程序有几个问题。

循环

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

您必须在其余可见部分中进行修改。 否则,此问题将继续存在。

使用清单!

与其使用原子或字符串,不如使用列表来表示单词。 最好是添加到您的.swiplrc.sicstusrc

:- set_prolog_flag(double_quotes, chars).

以这种方式,以下内容成立:

?- "abc" = [a,b,c].

避免割伤

以某种方式削减,有时可以工作,但是这样的程序很难调试。 特别适合初学者。 因此,不惜一切代价避免使用它们

使用简洁的算法

您正在使用高度模版的Prolog的“旧式”算法。 而是使用use_module(library(clpfd))获得更纯净的代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM