【问题标题】:List Length in PrologProlog 中的列表长度
【发布时间】:2013-10-07 17:02:16
【问题描述】:

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

【问题讨论】:

    标签: list prolog clpfd


    【解决方案1】:

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

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

    此代码为带有累加器的工作谓词提供种子,以 0 为种子。在每次递归时,它都会创建一个新的累加器,其值为当前值 + 1。到达列表末尾时,累加器的值为与期望的结果相统一。

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

    【讨论】:

      【解决方案2】:

      这段代码

      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 尚未实例化。

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          【解决方案4】:
          len([], LenResult):-
              LenResult is 0.
          
          len([X|Y], LenResult):-
              len(Y, L),
              LenResult is L + 1.
          

          【讨论】:

          • 虽然此代码可能会回答问题,但提供有关 why 和/或 如何 回答问题的额外上下文将显着改善其长期价值。请edit你的答案添加一些解释。
          【解决方案5】:

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

          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.
          

          在调用is 时,N1 已经实例化为一个整数,并且程序在没有错误的情况下完成。

          另见this other thread

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-05-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多