直接回答最初的问题,我做错了什么?...
当我运行原始代码时,任何预期的用例“无限期地运行”而没有产生结果。
通读主谓词:
decode([],[]).
这表示[] 是解码[] 的结果。听起来不错。
decode([H|T],L) :- makelist2(H,H2), append(H2,L,L2), decode(T,L2).
这表示L 是解码[H|T] 的结果,如果H2 是H 的扩展(这是makelist2 所做的......也许 - 我们将在下面讨论),和H2 附加到这个结果给出了另一个列表L2,它是原始尾部T 的解码形式。这听起来不正确。如果我解码[H|T],我应该(1)扩展H,(2)解码T给L2,然后(3)将H附加到L2给L。
所以修正后的第二个子句是:
decode([H|T], L) :- makelist2(H, H2), decode(T, L2), append(H2, L2, L).
注意append/3 的参数顺序,并且调用发生在尾部的decode 之后。正如鲍里斯之前指出的那样,append 和递归decode 的错误顺序会导致连续运行而没有任何输出,因为append 带有更多未实例化的参数会在decode 成功之前产生大量不需要的可能性。
但现在的结果是:
| ?- decode([[a,3]], L).
L = [a,a,a] ? ;
L = [a,a,a,a] ? ;
...
如果您在 Prolog 解释器中手动尝试我们的其他谓词,您会发现 makelist2/2 有问题:
它会产生正确的结果,但也会产生一堆不正确的结果。让我们看看makelist2/2。我们可以自己试试这个谓词,看看会发生什么:
| ?- makelist2([a,3], L).
L = [a,a,a] ? ;
L = [a,a,a,a] ? ;
...
有一个问题:makelist2/2 应该只给出第一个解决方案,但它继续运行,给出不正确的解决方案。让我们仔细看看makelist/2:
makelist2(L,L2) :- divide2(L,X,Y), makelist(X,Y,L2).
它采用[A,N] 形式的列表L,将其(通过divide2/3)划分为X = A 和Y = N,然后调用辅助makelist(X, Y, L2)。
makelist(_,N,[]):- N =< 0 .
makelist(X,Y,[X|Result]):-Y1 is Y-1,makelist(X,Y1,Result).
makelist/3 应该通过将第一个参数复制第二个参数中给出的次数来生成一个列表(第三个参数)。第二个,递归子句似乎没问题,但有一个重要缺陷:即使Y 的值小于或等于0,它也会成功。因此,即使找到了正确的解决方案,它也会在不正确的解决方案上继续成功,因为基本情况允许计数为 =< 0:
| ?- makelist(a,2,L).
L = [a,a] ? ;
L = [a,a,a] ? ;
我们可以修复makelist/2如下:
makelist(_,N,[]):- N =< 0 .
makelist(X,Y,[X|Result]):- Y > 0, Y1 is Y-1, makelist(X,Y1,Result).
现在代码将生成正确的结果。我们只需要修复decode/2的第二个子句和makelist/3的第二个子句。
| ?- decode([[a,3],[b,4]], L).
L = [a,a,a,b,b,b,b]
yes
仅包含这几处更正的完整原始代码如下所示:
divide(L, X, Y) :- length(X, 1), append(X, Y, L).
divide2(L, X, Y) :- divide(L, [X|_], [Y|_]).
makelist(_, N, []) :- N =< 0 .
makelist(X, Y, [X|Result]) :- Y > 0, Y1 is Y-1, makelist(X,Y1,Result).
makelist2(L, L2) :- divide2(L, X, Y), makelist(X, Y, L2).
decode([], []).
decode([H|T], L) :- makelist2(H,H2), decode(T,L2), append(H2,L2,L).
注意一些简单直接的改进。谓词 divide2(L, X, Y) 接受两个元素的列表 L 并产生每个单独的元素 X 和 Y。这个谓词是不必要的,因为在 Prolog 中,您可以通过简单的统一获得这些元素:L = [X, Y]。你可以在 Prolog 解释器中试试这个:
| ?- L = [a,3], L = [X,Y].
L = [a,3]
X = a
Y = 3
yes
然后我们可以完全删除divide/3 和divide2/3 谓词,并将对divide2(L, X, Y) 的调用替换为L = [X,Y] 并将makelist2/2 减少为:
makelist2(L, L2) :- L = [X, Y], makelist(X, Y, L2).
或者更简单(因为我们可以在子句的头部进行统一):
makelist2([X,Y], L2) :- makelist(X, Y, L2).
您可以删除makelist2/2 并直接从decode/2 调用makelist/2,方法是将H 直接与其两个元素[X, N] 统一起来。所以原代码简化为:
makelist(_, N, []) :- N =< 0 .
makelist(X, Y, [X|Result]) :- Y > 0, Y1 is Y-1, makelist(X,Y1,Result).
decode([], []).
decode([[X,N]|T], L) :- makelist(X, N, H2), decode(T, L2), append(H2, L2, L).
并且makelist/3 可以使用其他答案中提供的一种方法更清楚地执行(例如,请参阅 Boris 的 repeat/3 谓词)。