繁体   English   中英

列表长度在Prolog

[英]List Length in Prolog

我是Prolog编程的初学者。 我写了这个程序来计算列表的长度。 为什么以下程序错了?

length(0, []).
length(L+l, H|T) :- length(L, T).

我写下面的程序,它工作正常。

length([], 0).
length([H|T], N) :- length(T, N1), N is N1+1.

当我改变订单时,我收到了一个错误。 为什么?

length([], 0).
length([H|T], N) :- N is N1+1, length(T, N1).

这段代码

length_1(0,[]).
length_1(L+1, [H|T]) :- length_1(L,T).

作品(请注意添加的方括号),但以意想不到的方式。 它将构建一个表达式树,可以在以后进行评估:

?- length_1(X, [1,2,3]), L is X.
X = 0+1+1+1,
L = 3.

在您的重写代码(第二个版本)中,您会收到一个错误,因为在您调用/ 2时N1尚未实例化。

你需要使用累加器 虽然你可以做这样的事情:

list_length([]     , 0 ).
list_length([_|Xs] , L ) :- list_length(Xs,N) , L is N+1 .

它将一直递归到列表的末尾,然后,当每次调用返回时,向长度添加一个,直到它返回到具有正确结果的顶层。

这种方法的问题是每次递归都会在堆栈上推送一个新的堆栈帧。 这意味着,如果给出足够长的列表,您将[最终]耗尽堆栈空间。

相反,使用尾递归中介,如下所示:

list_length(Xs,L) :- list_length(Xs,0,L) .

list_length( []     , L , L ) .
list_length( [_|Xs] , T , L ) :-
  T1 is T+1 ,
  list_length(Xs,T1,L)
  .

此代码为一个带有累加器的worker谓词组成种子,接种为0.在每次递归时,它创建一个新的累加器,其值为当前值+ 1.当到达列表的末尾时,累加器的值与期望的结果。

prolog引擎足够聪明(TRO / Tail Recursion Optimization),可以看到它可以在每次调用时重用堆栈帧(因为在递归调用之后没有使用任何本地),从而将递归整齐地转换为迭代。

正如Carlo的回答所暗示的那样, L+1是Prolog术语,有两个参数, L1 ,其函子是+ Prolog不是一种函数式语言,因此您不能键入L+1并期望将其计算为总和。 尝试遵循Carlo的建议并使用is/2谓词来强制算术评估。 例如,调用目标X is 3 + 4将评估右操作数并尝试将结果与左操作数统一。 如果左操作数X是变量,则它将与7统一。 另请注意,在Prolog中,变量是单个赋值(但如果赋值的术语包含变量,则可以进一步实例化)。 也就是说,您只能将一个术语分配给变量一次。 当您回溯执行任务的目标时,撤消此分配。

len([], LenResult):-
    LenResult is 0.

len([X|Y], LenResult):-
    len(Y, L),
    LenResult is L + 1.

其他答案指出了相关问题,但我认为问题只是一个未实例化的变量。 在最后一个例子中

length([], 0).
length([H|T], N) :- N is N1+1, length(T, N1).

虽然我们可以在is的右侧有变量,但它们必须已经实例化为一个整数,以便Prolog可以执行算术运算。 在上面的示例中, N1尚未实例化,并且Prolog无法应用is仿函数。 问题中未提及错误类型,但它应该是instantiation_error或“参数未充分实例化”(如果在SWI-Prolog中)。

当您在第二个规则中反转目标时

length([], 0).
length([H|T], N) :- length(T, N1), N is N1+1.

N1已经实例化的时候一个整数is被调用,程序完成无误。

另见这个其他主题

暂无
暂无

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

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