List Prolog中的列表长度

List Prolog中的列表长度,list,prolog,clpfd,List,Prolog,Clpfd,我是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,

我是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.

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

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

您需要使用累加器。虽然你可以这样做:

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)
  .
它将一直递归到列表的末尾,然后在每次调用返回时,在长度中添加一个,直到返回到顶层并得到正确的结果

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

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

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递归优化),可以在每次调用时重用堆栈框架(因为递归调用后没有使用任何局部变量),从而将递归巧妙地转换为迭代。

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

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
函子。问题中未提及错误类型,但它应该是
实例化\u错误
或“参数未充分实例化”(如果在SWI Prolog中)

当你颠倒第二条规则中的目标时

length([], 0).
length([H|T], N) :- length(T, N1), N is N1+1.
调用
is
时,
N1
已被实例化为整数,并且程序在没有错误的情况下完成


另请参见。

尽管此代码可能会回答此问题,但提供有关其为什么和/或如何回答此问题的其他上下文将显著提高其长期价值。请在您的回答中添加一些解释。