简体   繁体   English

Prolog谓词解决方案

[英]Prolog Predicate Solution

I am going through some past exam questions for my prolog exam that is coming up. 我正在为即将到来的Prolog考试解决一些过去的考试问题。

Here is the question: 这是问题:

  1. (a) Write a predicate insert(Xs, Y, Zs) that holds when Zs is the list obtained by inserting Y into the list Xs . (a)编写一个谓词insert(Xs, Y, Zs) ,该谓词在Zs是通过将Y插入列表Xs中获得的列表时成立。 A query such as: 查询,例如:

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

should succeed four times and give the following answers: 应该成功四次,并给出以下答案:

Zs = [4, 1, 2, 3]

Zs = [1, 4, 2, 3]

Zs = [1, 2, 4, 3]

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

I'm a bit concerned because I have no idea where to start. 我有点担心,因为我不知道从哪里开始。 Would anyone be able to help out as I need example solutions to practice for my exam. 在我需要示例解决方案来练习考试的过程中,有人可以提供帮助吗?

Would really appreciate any help with this. 非常感谢您的帮助。

We start by changing the terrible name of this predicate: The predicate should describe what holds , not what to do. 我们从更改此谓词的可怕名称开始:谓词应描述什么成立而不是做什么。 The name should reflect that. 名称应反映出这一点。 I suggest list_with_element/3 , and encourage you to try finding even better names, ideally making clear what each argument stands for. 我建议使用list_with_element/3 ,并鼓励您尝试寻找更好的名称,最好是明确每个参数的含义。

Then, we do what we set out to do: Describe the cases that make this relation hold. 然后,我们要做我们打算做的事情:描述使这种关系成立的情况。

For example: 例如:

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

I leave filling in the ... as an easy exercise. 我留在填写...作为一个简单练习。 State the condition that is necessary for the clause head to be true! 说明子句头为真所必需的条件!

EDIT : I would like to say a bit more about the pattern above. 编辑 :我想说更多有关上述模式。 In my experience, a good way—and definitely in the beginning—to reason about predicates that describe lists is to consider two basic cases: 以我的经验,推论描述列表的谓词的一种好方法(绝对是在开始时)是考虑以下两种基本情况:

  1. the atom [] , denoting the empty list 原子[] ,表示空列表
  2. terms of the form '.'(E, Es) , also written as [E|Es] , where E is the first element of the list and Es is again a list. 形式为'.'(E, Es)术语,也写为[E|Es] ,其中E是列表的第一个元素, Es也是列表。

This follows the inductive definition of lists. 这遵循列表的归纳定义。

The drawback in this specific case is that this approach leads to a situation where case (2) again needs to be divided into two subcases, and somehow unexpectedly necessitates three clauses to handle the two basic cases. 在这种特定情况下的缺点是,这种方法导致情况再次需要将情况(2)分为两个子情况,并且以某种方式意外地需要三个子句来处理这两个基本情况。 This obviously runs counter to our intuitive expectation that two clauses should suffice. 显然,这与我们直觉地期望两个子句就足够相反。 Indeed they do, but we need to be careful not to accidentally lose solutions. 的确确实如此,但是我们需要注意不要意外丢失解决方案。 In this case, the first two clauses above are both subsumed by the fact: 在这种情况下,上面的前两个子句包含在以下事实中:

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

Every experienced Prolog coder will write such predicates in this way, or just, as in this case, use select/3 directly. 每个有经验的Prolog编码器都将以这种方式编写谓词,或者仅在这种情况下直接使用select/3 This is what @lurker sensed and hinted at, and @tas correctly shows that a different clause (which is easy to come up with accidentally) does not fully subsume all cases we want to express. 这就是@lurker的感知和暗示,而@tas正确地表明了一个不同的子句(很容易意外地想到)并没有完全包含我们要表达的所有情况。

Thus, I still find it a lot easier to think first about the empty list explicitly, make sure to get that case correct, then continue with more complex cases, and then see if you can write the existing program more compactly. 因此,我仍然发现首先明确考虑空列表,确保正确设置大小写,然后继续处理更复杂的情况,然后查看是否可以更紧凑地编写现有程序,要容易得多。 This is the way I also used for this sample code, but I did not make it as short as possible. 这也是我用于此示例代码的方式,但是我没有使其尽可能短。 Note that with monotonic code, it is completely OK to have redundant facts! 请注意,对于单调代码,有多余的事实是完全可以的!

Note that is is specifically not OK to just replace the first two clauses by: 请注意,是专门为没有确定,只是通过更换前两个条款:

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

because this clause does not subsume case (1) above. 因为此子句不包含上述情况(1)。

I guess that one answer that the question might be looking for goes along these lines: 我猜想这个问题可能正在寻找的一个答案遵循以下思路:

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

If you would like a declarative reading: 如果您希望声明式阅读:

NewList has Element between the front and the back of List. NewList在List的前后之间具有Element。

Check carefully if append/3 or a predicate with the same semantics appears in the earlier questions or the study material. 仔细检查是否在先前的问题或学习材料中出现了append/3或具有相同语义的谓词。

And note that this is in essence the exact same solution as the suggestion by @mat , if I understand it correctly. 并请注意,如果我正确理解的话,这实际上与@mat建议完全相同 Consult the textbook definition of append/3 for details. 有关详细信息,请查阅append/3的教科书定义。 Or even better, look at the textbook definition of append/3 and adapt it to use if for "inserting". 甚至更好的是,查看append/3的教科书定义, append/3其修改为在“插入”时使用。

There is a built-in predicate select/3 that does the same thing, although with the arguments in a different order. 尽管参数的顺序不同,但内置的谓词select/3可以执行相同的操作。

Remember that (if defined correctly) a predicate can work in different directions. 请记住,(如果定义正确)谓词可以在不同方向上工作。 For instance, it can tell you what a list would look like after removing an element, it can (although it's fairly trivial) tell you what element to remove from one list to get another, or it can tell you what lists, after having a given element removed, would resemble a given list. 例如,它可以告诉你一个列表看起来像移除元素后,它可以(尽管这也很简单)告诉您从一个列表中删除哪些因素再弄,也可以告诉你列出,有后给定元素被删除,将类似于给定列表。
(Hint: you may want to look into that last one). (提示:您可能要研究最后一个)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM