【问题标题】:Taking out the 2nd to last element - Prolog取出倒数第二个元素 - Prolog
【发布时间】:2026-01-22 15:20:26
【问题描述】:

我对 Prolog 很陌生,我想弄清楚这个(函数?)到底发生了什么,它取出了列表中倒数第二个元素。

remove([],[]).
remove([X],[X]).
remove([_,X],[X]).
remove([X|Xs], [X|Ys]) :-
   Xs = [_,_|_],
   remove(Xs,Ys).

我熟悉模式匹配,因为我在 SML 方面做过一些工作。第一个显然是基本情况,当我们分解它时返回空列表。当只剩下一个时,第二个返回相同的变量。第三个看起来好像返回了最后一个元素,而忽略了倒数第二个?至于归纳案例,如果......(这是我完全迷失的地方),它将把列表的头部附加到新列表中。谁能解释一下这个函数中发生了什么,以便我更好地理解该语言?

【问题讨论】:

    标签: prolog


    【解决方案1】:

    详细阐述 CapelliC 的解释:

    remove([],[]).
    

    空列表是删除倒数第二个元素的空列表。

    remove([X],[X]).
    

    单元素列表本身就是删除了倒数第二个元素。

    remove([_,X],[X]).
    

    删除倒数第二个元素的双元素列表是由一个元素组成的列表,该元素由双元素列表的最后一个元素组成。

    remove([X|Xs], [X|Ys]) :-
       Xs = [_,_|_],
       remove(Xs,Ys).
    

    第二个列表是第一个删除第二个元素的列表,并且共享相同的第一个元素,IF:

    1. 第一个列表的尾部至少包含两个元素,并且
    2. 第二个列表的尾部是第一个列表的尾部,删除了倒数第二个元素

    【讨论】:

      【解决方案2】:

      一组子句是一个谓词,或过程

      所有前三个都是基本情况,递归一个复制,而第一个列表中至少有 3 个元素。

      我会描述类似“删除最后一个元素”这样的行为。

      【讨论】:

        【解决方案3】:

        那么,如何以声明方式阅读

        remove([X|Xs], [X|Ys]) :-
           Xs = [_,_|_],
           remove(Xs,Ys).
        

        最重要的是您首先要了解:- 的实际含义。

        头部 :- 身体.

        这意味着:只要 Body 成立,我们可以得出结论,Head 也成立。注意箭头相当不直观的方向。它从右到左。而不是从左到右,当你总结某事时通常非正式地写。然而,错误指向了我们“从中”得到什么的方向。

        为了更好地看到这一点,您可以输入 Body 作为查询!

        ?- Xs = [_,_|_], remove(Xs,Ys).
        Xs = [A, B],
        Ys = [B] ;
        Xs = [A, B, C],
        Ys = [A, C] ;
        ...
        

        所以我们得到了所有答案,除了Xs 的元素少于两个的答案。

        请注意,在程序上,事情完全是在另一个方向发生的——这对初学者来说非常困惑。更重要的是,由于 Prolog 使用了两个“非传统”特性:时间回溯和变量——我的意思是 real 变量,意思是所有可能的术语——而不是你从命令式和函数式语言中知道的这些编译时构造.在这些语言中,变量是运行时值的持有者。具体的价值观。在 Prolog 中,变量也存在于运行时。有关更多信息,请参阅Difference between logic programming and functional programming

        还有一个问题,我不确定你是否理解。想想:

        ?- remove(Xs, [1,2]).
        Xs = [1, A, 2] ;
        false.
        

        这里删除了什么?没有什么!恰恰相反,我们在列表中添加了另一个元素。出于这个原因,remove/2 这个名字在 Prolog 中并不理想——它让我们想起了面向命令的编程语言,这些语言强制给出一些参数,而另一些参数是计算出来的。起初您可能认为这无关紧要,毕竟它只是一个名称。但是不要忘记,在编程时,您通常没有时间考虑所有这些。所以一个好的关系名称可能更可取。

        要找到一个,只需从以下类型开始:list_list/2,然后细化list_removed/2list__without_2nd_last/2

        【讨论】:

          【解决方案4】:

          注释:

          remove( []     , []     ) .  % removing the 2nd last element from the empty list yields the empty list
          remove( [X]    , [X]    ) .  % removing the 2nd last element from a 1-element list yields the 1-element list.
          remove( [_,X]  , [X]    ) .  % removing the 2nd last element from a 2-element list yields the tail of the 2-element list
          remove( [X|Xs] , [X|Ys] ) :- % for any other case...
            Xs = [_,_|_],              % * if the tail contains 2 or more elements, the list is 3 elements or more in length
            remove(Xs,Ys).             % we simply prepend the head of the list to the result and recurse down.
          

          需要注意的是,最后一个子句可以改写得更清楚一点(也更简洁一点):

          remove( [X1,X2,X3|Xs] , [X1|Ys] ) :- % for any other case (a list of length 3 or more)
            remove([X2,X3|Xs],Ys).             % we simply prepend the head of the list to the result and recurse down.
          

          或作为

          remove( [X1|[X2,X3|Xs]] , [X1|Ys] ) :- % for any other case (a list of length 3 or more)
            remove([X2,X3|Xs],Ys).               % we simply prepend the head of the list to the result and recurse down.
          

          【讨论】:

            最近更新 更多