【问题标题】:Prolog Predicate SolutionProlog 谓词解决方案
【发布时间】:2016-04-14 10:51:10
【问题描述】:

我正在为即将到来的序言考试复习一些过去的考试题。

问题来了:

  1. (a) 写一个谓词insert(Xs, Y, Zs),当Zs 是获得的列表时成立 通过将Y 插入列表Xs。查询如:

    ? - insert([1,2,3], 4, Zs).
    

应该成功四次并给出以下答案:

Zs = [4, 1, 2, 3]

Zs = [1, 4, 2, 3]

Zs = [1, 2, 4, 3]

Zs = [1, 2, 3, 4]. 

我有点担心,因为我不知道从哪里开始。任何人都可以提供帮助,因为我需要示例解决方案来练习我的考试。

非常感谢您对此提供任何帮助。

【问题讨论】:

  • insert(Xs, Y, Zs) 应该定义当Zs 是列表Xs 并在某处插入Y 时的样子。要定义规则,您需要将其分解为合乎逻辑的情况。您的主要定义将是递归的。但一个简单的基本情况是 Z 看起来就像 XsY 作为它的第一个元素 ([Y|Xs])。

标签: list prolog


【解决方案1】:

我们首先更改这个谓词的可怕名称:谓词应该描述什么是有效的不是要做什么。名称应该反映这一点。我建议list_with_element/3,并鼓励您尝试寻找更好的名称,最好弄清楚每个参数代表什么。

然后,我们做我们打算做的事情:描述使这种关系成立的案例。

例如:

list_with_element([], E, [E]).
list_with_element([L|Ls], E, [E,L|Ls]).
list_with_element([L|Ls0], E, [L|Ls]) :-
        ...

我将填写... 作为一项简单的练习。陈述子句标题为真的必要条件!

编辑:我想多谈谈上面的模式。根据我的经验,推理描述 列表 的谓词的一个好方法(而且肯定是在开始时)是考虑两种基本情况:

  1. 原子[],表示空列表
  2. '.'(E, Es) 形式的术语,也写作[E|Es],其中E 是列表的第一个元素,Es 又是一个列表。

这遵循列表的归纳定义。

这种特定情况的缺点是这种方法会导致情况 (2) 再次需要分为两个子情况,并且以某种方式意外地需要 三个 子句来处理 两个基本案例。这显然违背了我们直观的期望,即 two 子句就足够了。确实如此,但我们需要小心不要意外丢失解决方案。在这种情况下,上面的前两个子句 both 包含在以下事实中:

list_with_element(Ls, E, [E|Ls]).

每个有经验的 Prolog 编码器都会以这种方式编写这样的谓词,或者就像在这种情况下,直接使用 select/3。这是@lurker 感知和暗示的,@tas 正确地表明,一个不同的子句(很容易意外提出)完全包含我们想要表达的所有情况。

因此,我仍然发现先明确地考虑空列表要容易得多,确保该情况正确,然后继续处理更复杂的情况,然后看看是否可以更紧凑地编写现有程序。这也是我用于此示例代码的方式,但我没有让它尽可能短。请注意,对于单调代码,有冗余事实是完全可以的!

请注意,仅可以将前两个子句替换为:

list_with_element([L|Ls], E, [E|Ls]).

因为该子句不包含上述情况(1)。

【讨论】:

  • @mat 感谢您的帮助-您能否发布完整的解决方案以便我可以检查我的谓词? ..谢谢。
  • @lurker:第一条规则只匹配空列表作为第一个参数。第二条规则仅匹配具有至少一个元素作为第一个参数的列表。在list_with_element([L|Ls], E, [E|Ls]). 中,第三个参数不是第一个参数+元素E,而是第一个列表的尾部+E
  • @tas 是的,我获奖了。我的问题是为什么这两条规则不能用我建议的规则代替。
  • @lurker:如果我在我看来,在@mat 的建议解决方案中插入一个明显的递归目标并运行上述查询,我​​会从上面得到预期的解决方案。如果我注释掉第一条规则,我只会得到:[4,1,2,3], [1,4,2,3], [1,2,4,3]。如果我将前两条规则替换为您评论中的一条,我会得到:[4,2,3], [1,4,3], [1,2,4]。所以需要@mat 的第一条规则来获得所有 4 个解决方案。您建议的第一条规则是 replace 一个元素。当然,如果我们考虑到递归规则的相同主体。我在想list_with_element(Ls0,E,Ls). ;-)
  • @tas 抱歉,我的建议规则有误。它应该是 list_with_element(L, E, [E|L]). 试试这个而不是 mat 的前两条规则。
【解决方案2】:

我猜这个问题可能正在寻找的一个答案是这样的:

insert(List, Element, NewList) :-
    append(Front, Back, List), % split list in two
    append(Front, [Element|Back], NewList). % reassemble list

如果您想要声明式阅读:

NewList 在 List 的前后有 Element。

仔细检查append/3或具有相同语义的谓词是否出现在前面的问题或学习材料中。

请注意,如果我理解正确的话,这本质上与suggestion by @mat 完全相同。详情请查阅append/3的教科书定义。或者更好的是,查看 append/3 的教科书定义并将其修改为使用 if 用于“插入”。

【讨论】:

    【解决方案3】:

    有一个内置谓词select/3 做同样的事情,尽管参数的顺序不同。

    请记住(如果定义正确)谓词可以在不同的方向上起作用。例如,它可以告诉您删除一个元素后列表会是什么样子,它可以(尽管它相当简单)告诉您从一个列表中删除哪个元素以获得另一个,或者它可以告诉您哪些列表,删除给定元素后,将类似于给定列表。
    (提示:您可能需要查看最后一个)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-09-29
      • 1970-01-01
      • 1970-01-01
      • 2015-02-03
      • 1970-01-01
      相关资源
      最近更新 更多