取出倒数第二个元素 - Prolog

时间:2013-11-04 06:54:31

标签: prolog

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

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

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

4 个答案:

答案 0 :(得分:3)

阐述CapelliC的解释:

remove([],[]).

空列表是一个空列表,其中删除了倒数第二个元素。

remove([X],[X]).

单元素列表本身已删除倒数第二个元素。

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

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

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

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

  1. 第一个列表的尾部至少包含两个元素AND
  2. 第二个列表的尾部是第一个列表的尾部,其中第二个到最后一个元素已被删除

答案 1 :(得分:2)

一组子句谓词过程

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

我会描述'删除前一个元素'之类的行为。

答案 2 :(得分:2)

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

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使用两个“非传统”特征:按时间顺序回溯和变量 - 我的意思是真正的变量,意味着所有可能的术语 - 而不是从命令式和函数式语言中知道的这些编译时构造。在这些语言中,变量是运行时值的持有者。具体价值观。在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

答案 3 :(得分:0)

注释:

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.