recursion - 如何创建删除倒数第二个元素的 Prolog 谓词?

标签 recursion prolog predicate

我需要帮助创建一个谓词,该谓词删除列表中倒数第二个元素并返回用 Prolog 编写的列表。到目前为止我已经

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

这就是我所了解到的。我需要找出一种方法来递归地遍历列表,直到它只有两个元素长,然后重新组装要返回的列表。如果可以的话帮忙解释一下。

最佳答案

到目前为止你的定义是完美的!它有点太专业了,所以我们必须扩展它。但你的程序是一个坚实的基础。

您“仅”需要扩展它。

remove([],[]).
remove([X],[X]).
remove([_,X],[X]).
remove([X,_,Y], [X,Y]).
remove([X,Y,_,Z], [X,Y,Z]).
remove([X,Y,Z,_,Z2], [X,Y,Z,Z2]).
...

好的,您知道如何继续了。现在,让我们识别常见情况:

...
remove([X,Y,_,Z], [X,Y,Z]).
%       ^^^        ^^^  
remove([X,Y,Z,_,Z2], [X,Y,Z,Z2]).
%       ^^^^^         ^^^^^
...

所以,我们有一个共同的列表前缀。我们可以说:

Whenever we have a list and its removed list, we can conclude that by adding one element on both sides, we get a longer list of that kind.

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

请注意,:- 实际上是一个箭头。这意味着:只要右侧为真,左侧也为真。

等一下!事实真的如此吗?如何测试这个? (如果您只测试阳性案例,您总是会得到"is"。)我们没有时间想出一些测试案例,不是吗?因此,让 Prolog 为我们完成这项艰苦的工作吧!所以,Prolog,填空吧!

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

?- remove(Xs,Ys).                         % most general goal
   Xs = [], Ys = []
;  Xs = [A], Ys = [A]
;  Xs = [_,A], Ys = [A]
;  Xs = [A], Ys = [A]                     % redundant, but OK
;  Xs = [A,B], Ys = [A,B], unexpected     % WRONG
;  Xs = [A,_,B], Ys = [A,B]
;  Xs = [A,B], Ys = [A,B], unexpected     % WRONG again!
;  Xs = [A,B,C], Ys = [A,B,C], unexpected % WRONG
;  Xs = [A,B,_,C], Ys = [A,B,C]
;  ... .

人们很容易拒绝一切并从头开始。 但在 Prolog 中你可以做得更好,所以让我们冷静下来估计一下实际的损害: 有些答案是不正确的。有些答案是正确的。

我们当前的定义可能有点过于笼统。

为了更好地了解情况,我将详细查看意外成功 remove([1,2],[1,2])。谁是罪魁祸首?

即使下面的程序切片/片段也会成功。

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

虽然这是我们程序的专门化,但它的内容是:remove/2 适用于所有相同的列表。这不可能是真的!为了解决这个问题,我们必须在剩余的可见部分做一些事情。我们必须将其特化。这里的问题是递归规则也适用:

remove([1,2], [1,2]) :-
   remove([2], [2]).
remove([2], [2]) :-
   remove([], []).

必须避免得出这样的结论。我们需要通过添加另一个目标 (=)/2 将规则限制为列表中至少有两个元素的情况。

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

那么我们的错误是什么?在非正式场合

Whenever we have a list and its removed list, ...

术语“已删除列表”含糊不清。这可能意味着我们在这里引用关系remove/2(这是不正确的,因为 remove([],[]) 成立,但仍然没有删除任何内容),或者我们在这里引用删除了元素的列表。此类错误在编程中不可避免地会发生,因为您希望通过使用比 Prolog 本身不太正式的语言来保持直觉。

作为引用,这里再次(并与其他定义进行比较)是最终定义:

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

有更有效的方法可以做到这一点,但这是最直接的方法。

关于recursion - 如何创建删除倒数第二个元素的 Prolog 谓词?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19669521/

相关文章:

计算 e^x 的 Haskell 函数

prolog - O(1) 术语查找

prolog - 什么东西永远不等于自己?

java - 使用 CriteriaBuilder 过滤多个 ENUM 值

c# - IDictionary<TKey, TValue> 的 LINQ?有没有我似乎找不到的 AddIf?

javascript - 比较对象并提取深度差异

java - 汉诺塔,工作但不应该

javascript - 什么 Javascript 库可以针对对象评估类似 MongoDB 的查询谓词?

c++ - 从堆栈中删除多余的变量 - C++

java - 如何将我的 Java UI 连接到 JPL Prolog 应用程序?