在列表中查找特定的元素序列,prolog

时间:2017-02-22 00:30:12

标签: list prolog

我必须编写一个带有List的谓词,如果列表在列表中的任何位置包含元素“a,b,c”,则必须成功,否则它将失败。我很遗憾在哪里开始(不是寻找解决方案,只是提示正确的方向)。

3 个答案:

答案 0 :(得分:4)

声明性措辞

几乎总是,当Prolog任务以相当命令式方式制定时,解决方案将相对有限。这意味着我们通常只能在几种模式和方向上使用它,而其他模式甚至可能产生错误的结果。

因此,我建议使用更多声明性措辞。

你说:

  

如果列表包含元素“a,b,c”,那么获取列表成功的谓词列表,否则失败

这是一种相当程序的方式来看待这个。请注意,在Prolog中,任何参数也可以是逻辑变量,因此甚至可能没有“take”列表。相反,我们希望谓词在这些情况下生成这样的列表!

注意你的措辞!通常,当您能够以声明方式表达任务时,优雅而通用的Prolog解决方案将是直截了当的,并且通常非常自然地遵循任务描述。

描述解决方案

首先,让我们关注持有什么。没有必要表达不包含的内容,因为在这种情况下,谓词无论如何都不会成功

我们想要描述

基本上,我们希望描述形式的{/ 1}}列表。

已经有一些答案,有各种缺点。

方式使用来自Indexing dif/2的元谓词[...,a,b,c,...]

abc([X,Y,Z|Vs]) :-
        if_((X=a,Y=b,Z=c), true, abc([Y,Z|Vs])).

概论

这适用于所有方向。首先,让我们尝试最常规的查询,其中单个参数是一个新的变量

?- abc(Vs).
Vs = [a, b, c|_5032] ;
Vs = [a, b, a, b, c|_5144] ;
Vs = [a, b, a, b, a, b, c|_5286] .

因此,我们可以生成解决方案,这是关系的一个非常好的属性!

谓词是单调,因此迭代加深可以公平地枚举答案:

?- length(Vs, _), abc(Vs).
Vs = [a, b, c] ;
Vs = [a, b, c, _11600] ;
Vs = [a, a, b, c] ;
Vs = [_11982, a, b, c],
dif(_11982, a) ;
Vs = [a, b, c, _11600, _11606] .

由此可见,无解决方案少于3个元素。在这种情况下,这是非常明显的。在其他情况下,从任务描述中可能不太明显这样的结果。

效率

如果谓词充分实例化,则谓词确定性

例如:

?- abc([a,b,c]).
true.

?- abc([z,a,b,c]).
true.

?- abc([a,b,c,z]).
true.

请注意,在这些情况下,没有选择点仍然存在!

答案 1 :(得分:2)

以下是您可以采取的三种方法,按灵活性大致按升序排列:

首先,是使用谓词nth0/3来查找列表中a,b和c的位置,然后检查<的位置。 b的位置< c的位置对于列表中a,b和c的多个实例(例如[c,b,a,b,c,a]),nth0将依次找到每个匹配元素的位置,这样如果有三个位置适合于标准(即使它们不是第一个位置),谓词也会成功。

提示1.1:nth0的语法,用于查找。

的位置
  

nth0(PositionA,[c,b,a,b,c,a],a)

提示1.2:语法小于(完整性)

  

PositionA < PositionB

部分解决方案1:使用nth0检查a,b和c在列表[c,b,a,b,c,a]中以某种顺序出现的一系列命令(汇总谓词留给你)

  

nth0(PositionA,[c,b,a,b,c,a],a),
 nth0(PositionB,[c,b,a,b,c,a],b),
 nth0(PositionC,[c,b,a,b,c,a],c),
 PositionA < PositionB,
 PositionB < PositionC.

第二种方法使用列表模式匹配 - 我们观察到,当下到列表时,我们必须遇到a,然后是b,然后是c。为此,我们可以构造三个查找a,b和c的谓词,然后在适当的位置传递列表的其余部分。我们必须构造这些谓词,以便在看到目标之前忽略其他元素。

提示2.1:谓词的头部,其中a是列表的第一个元素

  

find_a([a|Rest]) :-

提示2.2:谓词的头部,其中任何内容都是列表的第一个元素

  

find_a([_|Rest]) :-

提示2.3:当我们找到a时,我们开始寻找b

  

find_a([a|Rest]) :-
 find_b(Rest).

提示2.4:当我们找不到时,我们一直在寻找

  

find_a([_|Rest]) :-
 find_a(Rest).

提示2.5:订单很重要(实物)

  

如果我们先将find_a([a|Rest])置于知识库中,那么Prolog将始终首先尝试统一它,因此我们将匹配我们找到的第一个。如果我们把它放在第二位,这仍然有用,但是有很多额外的回溯,我们会发现每个都是相反的顺序。

提示2.6:不要忘记基本情况!

  

请记住,即使你找到c后你不需要做任何事情,你仍然需要创建一个事实,说明它是列表的头部:find_c([c|_]).

第三种方法本质上是第二种方法的通用版本 - 而不是创建谓词来查找a,b和c,而是创建一个按顺序查找元素列表的谓词。

提示3.1:您的谓词应该采用两个列表并比较每个

的头部
  

compare([A|Targets],[B|Checks]) :-

提示3.2:如果相同的变量名出现在多个位置,则它必须具有相同的谓词值才能匹配

  

compare([A|Targets],[A|Checks]) :- % succeeds when the same element is at the head of each list

提示3.3:如果匹配,请继续按两个列表

  

compare(Targets,Checks).

提示3.4:如果它们不匹配,只需查看检查清单

  

compare([A|Targets],Checks).

提示3.5:永远不要忘记基本情况(当没有更多目标时)

  

compare([],_).

提示3.6:和以前一样,订购仍然很重要

  

compare([A|Targets],[A|Checks]) :- ...应位于compare(Targets,[_|Checks]) :- ...

之前的知识库中

解决方案3:

  

compare([],_).
 compare([A|Targets],[A|Checks]) :-
 compare(Targets,Checks).
 compare(Targets,[_|Checks]) :-
 compare(Targets,Checks).

希望这有帮助!

答案 2 :(得分:1)

找到a,b,c

要按顺序查找列表中的字母a,b,c,应该从@lurker的评论开始,该评论显示[X, Y, Z | T]

has_abc([a,b,c|T]).

由于我使用的是SWI-Prolog而不愿意收到警告

  

警告:somecode.pl:           单例变量:[T]

我会通过将T更改为_

进行一些小改动
has_abc([a,b,c|_]).

然后运行一些简单的测试

?- has_abc([a,b,c]).
true.

?- has_abc([a,b,c,z]).
true.

?- has_abc([z,a,b,c]).
false.

正如您所看到的,谓词has_abc可以在列表的开头找到a,b,c,但不能在其他地方找到。{/ p>

列出一个部分

在Prolog中,可以使用[H|T]

以递归方式解构列表
  

deconstruct_list([Head | Tail]): -          写('列表头:'),写(头),nl,          deconstruct_list(尾部)。

和一些示范案例

?- deconstruct_list([]).
false.

?- deconstruct_list([a]).
Head of list: a
false.

?- deconstruct_list([a,b]).
Head of list: a
Head of list: b
false.

?- deconstruct_list([a,b,c]).
Head of list: a
Head of list: b
Head of list: c
false.

将谓词放在一起

现在结合前两个谓词来查找a,b,c并解构列表给我们

  

has_abc([a,b,c | _])。
     has_abc([_ | T]): -          has_abc(T)。

和一些测试用例

?- has_abc([]).
false.

?- has_abc([a]).
false.

?- has_abc([a,b]).
false.

?- has_abc([a,b,c]).
true .

?- has_abc([z,a,b,c]).
true .

?- has_abc([a,b,c,z]).
true .

?- has_abc([z,a,b,c,z]).
true .

用剪切解决选择点

几乎就在那里。有一个小问题,因为对于true答案,我们必须按Enter退出,这表示我们有一个选择点。

解决这个问题的一种方法是使用cut(!)表示一旦我们有一个答案停止寻找更多答案。

  

has_abc([a,b,c | _]): - !.
     has_abc([_ | T]): -          has_abc(T)。

和一些测试用例

?- has_abc([]).
false.

?- has_abc([a]).
false.

?- has_abc([a,b]).
false.

?- has_abc([a,b,c]).
true.

?- has_abc([z,a,b,c]).
true.

?- has_abc([a,b,c,z]).
true.

?- has_abc([z,a,b,c,z]).
true.

?- has_abc([d]).
false.

?- has_abc([d,e]).
false.

?- has_abc([d,e,f]).
false.

?- has_abc([d,e,f,g]).
false.

请注意,在运行测试用例时,无需按Enter即可结束查询。

解决没有削减的选择点

通过mat

查看答案