【问题标题】:Prolog - Recursive append to list returning falseProlog - 递归追加到返回 false 的列表
【发布时间】:2020-09-27 18:42:54
【问题描述】:

标题说明了一切,但我们又来了。尝试以递归方式附加到 Prolog 中的列表,虽然我之前已经通过“临时缓冲区”(通过 nb_setval/nb_getval)使其工作,但我想学习如何以稍微更合适的方式递归地附加到列表。

我知道 Prolog 可以在所有绑定中工作,一旦绑定了某些东西就很难操作它,所以一开始我就坐在那里,但我明白为什么这不起作用:

recursiveAppend([], _).
recursiveAppend([H|T], Output):-
    append(H, [], Output),
    recursiveAppend(T, Output).

这让我更改了代码并转到以下内容:

recursiveAppend([], _).
recursiveAppend([H|T], Output):-
    append(H, Output, NewOutput),
    recursiveAppend(T, NewOutput).

我曾希望这能奏效,因为这对我自己很有意义,显然对其他人也很有意义,同时也搜索了其他 StackOverflow 问题。不幸的是,在 SWI-Prolog 中调用这个谓词只会返回 false

?- recursiveAppend([1, 2, 3, 4, 5], L1). false

在这种情况下,预期/期望的结果是:

?- recursiveAppend([1, 2, 3, 4, 5], L1). L1 = [1, 2, 3, 4, 5].

为了清楚起见,如果“充实”,程序的运行时应该如下所示:

recursiveAppend([H|T], Output):-
    % H is 1, Output is []
    append(H, Output, NewOutput),
    % NewOutput is [1]
    recursiveAppend(T, NewOutput).

recursiveAppend([H|T], Output):-
    % H is 2, Output is [1]
    append(H, Output, NewOutput),
    % NewOutput is [1, 2]
    recursiveAppend(T, NewOutput).

recursiveAppend([H|T], Output):-
    % H is 3, Output is [1, 2]
    append(H, Output, NewOutput),
    % NewOutput is [1, 2, 3]
    recursiveAppend(T, NewOutput).

recursiveAppend([H|T], Output):-
    % H is 4, Output is [1, 2, 3]
    append(H, Output, NewOutput),
    % NewOutput is [1, 2, 3, 4]
    recursiveAppend(T, NewOutput).

recursiveAppend([H|T], Output):-
    % H is 5, Output is [1, 2, 3, 4]
    append(H, Output, NewOutput),
    % NewOutput is [1, 2, 3, 4, 5]
    recursiveAppend(T, NewOutput).

recursiveAppend([], _). % First argument (list) is empty, and the second argument (list) has been populated (with [1, 2, 3, 4, 5]), program done.

感谢任何和所有帮助,即使这个问题可能已经被问过一百万次了!

【问题讨论】:

  • 它附加了什么?目前还不清楚。该示例没有说明:recursiveAppend([1, 2, 3, 4, 5], L1), L1 = [1, 2, 3, 4, 5]. 可以简单地编码为recursiveAppend(A, L1) :- L1 = A.。请给出更多不能被误解的例子,并更清楚地表明你的意图。
  • @WillNess 现在已经扩展了这个问题,希望能够帮助您理解我想要实现的目标?
  • 不,您还没有向我们展示我要求的输入-输出配对的“更多示例”。 “输入”表示“这是我在 REPL 上尝试的测试调用”,“输出”表示“这是我希望看到的响应”。您似乎不了解基本的 Prolog。从一开始就从教程开始。就你不理解的每一个最小的可能的事情提出问题,不要在流沙地基上建造城堡,然后问我们为什么这个大型结构不正确,或者根本不正确。当你学习时,通过编写小程序并在 REPL 中尝试,验证你不断增长的理解中的每一个新步骤。

标签: prolog


【解决方案1】:

“递归追加”在 Prolog 中通常没有意义。该问题应包含有关您要解决的问题的信息。目前不是这样。它是关于你如何尝试解决你的问题。那个“如何”是“递归追加”,但这几乎肯定不是你应该如何真正解决这个问题。如果我们知道问题是什么,而不是你认为你想如何解决它,我们可以提供更好的帮助。

https://stackoverflow.com/a/64092447/4391743的问题和解决方案为例:

?- recursiveAppend([1, 2, 3], Ys).
Ys = [1, 2, 3].

?- recursiveAppend(Xs, [1, 2, 3]).
Xs = [1, 2, 3] ;
% nontermination after first answer

?- recursiveAppend([1, 2, X], [A, B, 3]).
X = 3,
A = 1,
B = 2.

?- recursiveAppend([1, 2 | Rest], [A, B, 3, 4]).
Rest = [3, 4],
A = 1,
B = 2 ;
% nontermination after first answer

如果这是您想要的,那么您似乎想要的是“列表副本”谓词。这里有一个更短、更快、更完整的:

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

这没有上述谓词所具有的非终止问题:

?- list_copy([1, 2, 3], Ys).
Ys = [1, 2, 3].

?- list_copy(Xs, [1, 2, 3]).
Xs = [1, 2, 3].

?- list_copy([1, 2, X], [A, B, 3]).
X = 3,
A = 1,
B = 2.

?- list_copy([1, 2 | Rest], [A, B, 3, 4]).
Rest = [3, 4],
A = 1,
B = 2.

如果一个参数是一个列表,另一个是一个变量,则会建立一个新的列表结构并绑定到这个变量。

但是...为什么你需要一个新的列表结构呢?在纯 Prolog 中,您无法判断两个术语是否相同(即共享相同的内存位置)或“只是”在结构上相等。您通常也不关心(或不应该)。 (在非纯 Prolog 中有关于共享和显式复制的知识的用途,但同样我们不知道您要做什么。)

因此,如果我们无法判断“副本”是否确实是副本或只是“一个相等的术语”,那么我们根本不需要复制。我们可以通过统一得到与上面完全相同的行为:

?- [1, 2, 3] = Ys.
Ys = [1, 2, 3].

?- Xs = [1, 2, 3].
Xs = [1, 2, 3].

?- [1, 2, X] = [A, B, 3].
X = 3,
A = 1,
B = 2.

?- [1, 2 | Rest] = [A, B, 3, 4].
Rest = [3, 4],
A = 1,
B = 2.

不需要复制,当然也不需要“递归追加”来实现统一,Prolog 知道如何为您进行统一。

如果这不是您想要的,请告诉我们实际问题是什么。 “递归追加”几乎肯定不是它。

【讨论】:

  • BTW OP 的代码可以使用same-length tracking to improve termination 进行调整。有了它,recursiveAppend(Xs, [1, 2, 3]). 回溯失败。出于某种原因,仍然存在选择点,与您的 list_copy/2 不同,这 无论如何,自上而下,线性地做这种事情的正确方法,因为 Prolog 非常好at 与它的 tail-rec-mod-cons; a.o.t.二次重复追加。但当然对于 那种 类型的讨论,OP 应该经过一些了解,而不仅仅是工作代码。 :|
  • (将附加参数放在第一个位置fixed that problem。)
  • 感谢这些实验。奇怪的是,将 SameLength 跟踪置于参数位置 1、2 或 3 会固定选择点,但将其置于参数位置 4 会使选择处于打开状态。当 SWI 只索引第一个参数时,它更容易理解。
  • 是的,这确实取决于版本。在本地,我安装了一个相当旧的版本,即使使用第二个也无法正常工作。 :)
  • 我想在任何时候都没有/没有任何意义或问题,而只是一个练习,看看 Prolog 如何处理递归和列表(即使对我来说,在 Prolog 中创建一个重要的程序也很容易,但我开始意识到我有更多的事情要考虑引入第二个列表)。我对 Prolog 还是很陌生,但与 OOP 相比,我正在慢慢尝试了解事物的工作方式(和不工作方式)。谢谢你的回答!
【解决方案2】:

Prolog 是一种不同的编程范式。它要求您“忘记”您对编程的所有了解并以开放的心态学习。不要在使用“普通”变量并重新影响不同值时尝试学习 Prolog,Prolog 变量只有一个值或没有。它们可能仅在回溯时采用不同的值,并尝试为程序中的所有变量找到另一组满足所有给定谓词的值。

建议您阅读“立即学习 Prolog”之类的书籍。大量来自州立大学的教程可在互联网上免费获得。

根据您最近对调用 recursiveAppend 的示例进行的编辑,下面是符合示例的代码。

recursiveAppend(X, Y) :- recursiveAppend(X, [], Y).

    recursiveAppend([], X, X).
    recursiveAppend([H|T], Current, Output):-
        append(Current, [H], NewTemp),
        recursiveAppend(T, NewTemp, Output).

您之前的代码返回 false,因为 append 需要列表作为参数。所以附加一个整数(输入列表的项目)总是会失败。我创建了一个版本 recursiveAppend/3 来在第二个参数中累积当前列表。在列表的末尾,当前列表成为最终输出。您能否通过更多示例进一步测试它并告诉我们它是否按要求工作。

【讨论】:

  • 我确实提到我希望它“递归附加”;但你是对的,我实际上想要它做什么并不完全清楚。现在已在问题中添加了“预期/期望”“部分”,希望对您有所帮助?
  • 举一个或多个例子
  • 如果我错了,请纠正我,但我相信我有?我有代码 sn-ps 代码、我用来调用谓词的确切行、预期/期望以及当前结果?它不一定是大部分代码的一部分,而是一个逐步学习如何做事的小任务,所以我不知道我还能给你什么。
  • 我不相信说“忘记你所知道的一切”是有帮助的。我知道这是一句流行的说法,但我们仍然可以说 Prolog-as-functional-language 基础是一种具有命名指针的一次性语言,没有嵌套范围,围绕状态传递范式构建。
  • @peter.cyc 现在已经测试了您提供的代码,据我所知,它非常符合我的要求。我也喜欢你的回答,因为(我相信)我明白为什么我的解决方案在你的解决方案成功的地方失败了。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-04-10
  • 2020-05-05
  • 2017-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多