2

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

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

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

4

4 回答 4

3

详细阐述 CapelliC 的解释:

remove([],[]).

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

remove([X],[X]).

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

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

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

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

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

  1. 第一个列表的尾部至少包含两个元素,并且
  2. 第二个列表的尾部是第一个列表的尾部,删除了倒数第二个元素
于 2013-11-04T11:54:37.663 回答
2

一组子句谓词过程

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

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

于 2013-11-04T07:09:39.020 回答
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 中,变量也存在于运行时。有关更多信息,请参阅逻辑编程和函数式编程之间的区别

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

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

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

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

于 2013-11-05T01:16:33.953 回答
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.
于 2013-11-05T00:14:40.560 回答