【问题标题】:Will using member within a forall clause in SWI-Prolog always output the elements in the same order?在 SWI-Prolog 的 forall 子句中使用成员是否总是以相同的顺序输出元素?
【发布时间】:2014-02-27 05:34:20
【问题描述】:

最近进入 Prolog,我一直在使用它完成一些简单的任务,并开始想知道如何在 forall 循环中使用 member,如下面的简单示例中的那个:

forall(member(A,[1,2,3,4]), print(A)).

在你做这样的事情的情况下,forall 是否总是会在每次调用时以相同的顺序处理列表中的元素?是否必须通过执行以下操作来强制执行:

A = [1,2,3,4], sort(A, B), forall(member(C,B), print(C)).

根据我最初所做的少量研究,我猜测它归结为 member/2 的行为,但 SWI-Prolog 网站上的函数文档非常简短。然而,它确实提到了关于 member/2 的确定性,这让我意识到我可能是在正确的道路上说它总是以相同的顺序提取元素,尽管我还不确定。

谁能给我任何关于这个的保证或解释?

【问题讨论】:

  • 我认为你可以依赖它,因为 Prolog 列表是单链接的,任何其他遍历都会表现得更差。我当然看不出与首先命名列表有任何语义差异。

标签: prolog swi-prolog iso-prolog


【解决方案1】:

Non-determinism 在 Prolog 中只是指一个谓词可能有多个解决方案。显然,member/2 就是这样一个谓词。这并不意味着您必须担心您的计算变得不可预测。 Prolog 有一个定义明确的计算规则,它本质上说以深度优先、从左到右的方式探索替代解决方案。因此,您的目标 member(X,[1,2,3,4]) 将以预期的 1、2、3、4 顺序生成 X 的解决方案。

对列表 [1,2,3,4] 进行排序不会有任何区别,因为它已经排序(根据 Prolog 的标准术语顺序)。

关于forall/2 的警告:一些 Prolog 定义了这一点,但它可能没有你想象的那么有用,因为它并不是真正的“循环”。您可以在示例中使用它,因为您只在每次迭代中执行打印副作用。对于大多数其他目的,您应该熟悉递归模式,例如

print_list([]).
print_list([X|Xs]) :- print(X), print_list(Xs).

【讨论】:

  • +1 不错,这可能就是 OP 的想法
  • 谢谢@jschimpf,现在更有意义了。还要感谢关于递归的建议,这更多是关于使用 forall 时会发生什么的理论的问题,但我确实知道递归通常是一个更好的主意。
  • 这是不正确的。 Nondeterministic algorithm,另见question。事实元组是确定性的,因为它在调用时总是返回相同的输出。成员是半确定性的,因为它为相同的输入提供相同的输出。非确定性意味着运行之间相同输入的输出可能不同,例如在给定范围内生成随机数。
【解决方案2】:

N208 定义了 forall/2 + (call(Generator), + call(Test)),所以这使它不那么可疑。但是由于 ISO 核心标准 (+)/1 已经调用/1 并且 ISO 核心标准 (,)/2 将进行主体转换,因此可以在 ISO 核心标准 Prolog 中简单地定义如下:

forall(生成器,测试):-
\+(生成器,\+ 测试)。

SWI-Prolog也实现了这种方式,使用forall/2时不会看到Ulrich Neumerkel观察到的错误:

Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.18)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.

?- \+ ( C= !, \+ (C,fail;writeq(nonconforming))).
nonconforming
true.

?- forall(C=!, (C,fail;writeq(nonconforming))).
false.

旁白:

我不知道 for 循环有多大用处。在我看来,将它用于循环不是正确的方法,因为测试可能会失败,然后构造也会失败。我还通过Striegnitz and Blackburn 看到了他们称为故障驱动循环的辅助谓词的以下定义。

doall(目标):-
目标,失败。
doall(_)。

我发现自己直接写 Goal, fail; true 也可以完成这项工作:

Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.18)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.

?- member(A,[1,2,3,4]), write(A), nl, fail; true.
1
2
3
4
true.

【讨论】:

    【解决方案3】:

    严格来说,SWI 在几个层面上都没有保证:

    1mo,member/2forall/2 将以这种方式执行,因为您可以重新定义它们。

    ?- [user].
    member(X,X).
    |: % user://1 compiled 0.00 sec, 2 clauses
    true.
    
    ?- forall(member(A,[1,2,3,4]), print(A)).
    [1,2,3,4]
    true.
    

    但是,member/2 是在 Prolog prologue 中定义的,它涵盖了您感兴趣的所有细节。 至于forall(A,B),改写\+ (A, \+B) 更安全,因为这仅依赖于标准功能。 forall/2 本身并没有定义,因此很难判断什么是“正确”的行为。

    2do,SWI 将符合标准。如果您阅读文档,您会注意到标准一致性没有自我声明(例如 SICStus Prolog)。事实上,\+ (A, \+B) 并不完全符合,如下例所示,它应该静默失败,而是打印 nonconforming

    ?-  \+ ( C= !, \+ (C,fail;writeq(nonconforming))).
    

    【讨论】:

    • forall/2 在 B-Prolog、BinProlog、CxProlog、GNU Prolog、JIProlog、Lean Prolog、LPA WinProlog/MacProlog、Qu-Prolog、SWI-Prolog、XSB 中定义为内置谓词,雅普。它也在 2009 年 10 月的 ISO Prolog Core 修订草案提案中进行了指定。在 2009 年的 WG17 会议上投票赞成将其纳入标准。它似乎在一段时间后被放弃了。但这并不排除将其视为事实上的标准谓词。
    • @PauloMoura:除了 GNU Prolog,您的列表中没有一个声明自己是 ISO,这使得 forall/2 的定义可能意味着什么是开放的。例如。 YAP:forall((fail,2),true) 成功而不是产生错误。
    • 只有 YAP 上的顶级。在文件中定义的谓词子句的主体中编译相同的调用,您会收到编译时错误。像这样的边缘情况,对于大多数使用场景来说,都不是问题。
    • @PauloMoura:所以 YAP 不符合 ISO+N208 中的定义。它的行为也不一致。 “边缘情况”既不是 ISO 也不是 N208 的概念。
    • 这是一个 SWI7 错误,您的查询写入不合格。所以我想我应该再次投票。我首先误解了你的帖子。有趣的发现。在我的系统、ECLiPSe prolog、SICStus Prolog 等中运行良好,所有这些都给出了正确的编号。
    猜你喜欢
    • 2011-05-05
    • 2010-11-09
    • 1970-01-01
    • 1970-01-01
    • 2023-02-14
    • 2020-06-22
    • 2015-04-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多