Prolog中列表连接程序的声明性解释

时间:2013-03-19 17:59:21

标签: prolog declarative

我有一个非常简单的问题:编写一个Prolog程序来实现附加的Prolog函数,该函数连接两个字符串并按以下方式工作:

  1. 追加([A,B],[C,d],X)。 ---> X = [a,b,c,d]
  2. 追加([A,B],X,[A,B,C,d])。 ---> X = [c,d]
  3. 追加([A,B],[X,d],[A,B,C,d])。 ---> X = C
  4. 追加(X,Y,[A,B,C,d])。 ---> X = []和Y = [a,b,c,d)
  5. 所以我有以下两种解决方案,我不确定我的陈述性解释是否正确:

    1)解决方案1:

    myappend1([],L,L).
    
    myappend1([X|L1],L2,[X|L3]) :- myappend1(L1,L2,L3).
    

    我认为我可以用声明的方式阅读它:

    事实上说:如果第一个列表(L1)为空且第二个列表(L2)不为空,则为TRUE ,L1 * L2的串联为L2

    如果事实并非如此,则意味着第一个列表不为空,因此第一个列表和第二个列表的串联不是真的,这是第二个列表

    所以,让我调用第一个列表L1,第二个列表L2和第三个列表L3然后如果L3是L1和L2的串联则规则响应为TRUE,否则返回

    我认为这条规则的陈述含义是:如果规则的主体是真的,则规则的头部是真的。

    • 在头部提取L1列表和L3列表中的第一个X元素(并尝试统一,如果匹配则继续,否则它意味着第三个列表不是第一个和第二个列表的串联)
    • 在正文中调用第一个列表中没有X元素的函数,第二个列表和L3列表(代表串联)

    当它到达我已经证明事实的基本情况 myappend1([],L,L)。这是真的,程序会在之前的过去进行回溯,因为X元素第一个列表与第三个列表的X元素统一,它可以做到这个计算传递它是TRUE并返回到达第一个断言

    这是正确的陈述性解释吗?

    2)第二个解决方案:

    myappend2([],L,L).
    
    myappend2(L1,L2,L3) :-  L1=[X|T],               % Dimostra questo predicato AND
                        L3=[X|L4],      % Dimostra questo predicato AND
                myappend2(T,L2,L4).     % Dimostra questa funzione
    

    与前面的解决方案一样,事实只是说:如果第一个列表(L1)为空而第二个列表(L2)不为空,则为TRUE 表示L1 *的串联L2是L2

    如果事实并非如此,则意味着第一个列表不为空,因此第一个列表和第二个列表的串联不是真的,这是第二个列表

    如果事实并非如此,则Prolog会调用该规则,而此规则意味着:如果规则的正文为真,则规则的头部为真。

    在这种情况下,我可以这样阅读:

    L1和L2的串联是L3,如果它是真的,则为TRUE:

    • L1的当前第一个X元素与串联列表的当前第一个元素统一,myappend2在第一个子列表上调用,L2和第三个子列表是真的

    这是对的吗?

    对我而言,以陈述的方式推理是如此困难: - (

2 个答案:

答案 0 :(得分:2)

与上次一样,您正在添加代码中不存在的限制。不要为此感到难过,Prolog是非常不同的,需要时间来适应它。

让我们开始。

append([], L, L).

你说:

  

如果第一个列表(L1)为空且第二个列表(L2)不为空,则L1 * L2的串联为L2

为TRUE

事实上,这条规则说 nothing 关于L2是否为空 - 甚至是列表! - 或者不是。它只是说附加到其他东西的空列表是其他东西。观察:

?- append([], foo, X).
X = foo.

这里的声明性读数是“附加到L的空列表是L。”

  

如果事实并非如此,则意味着第一个列表不为空,因此第一个列表和第二个列表的串联不是真的,这是第二个列表

是的,这是正确的,但Prolog并没有深入探究身体。它只是说“第一个列表不是空的,所以这个规则不匹配;继续前进。”

下一条规则:

myappend1([X|L1], L2, [X|L3]) :- myappend1(L1,L2,L3).

你的评论对我来说似乎过于复杂。我会说这个规则说:“如果列表L1到L2的myappend1是L3,则列表的[mysepend1] [X后跟L1]到L2是列表[X后跟L3]。”然而,这种阅读的后果正如你所描述的那样。

因此,您对第一版中发生的事情的理解是正确的。

第二种解决方案在机械上与第一种解决方案完全相同。唯一的区别是我们已经将统一从条款的头部移到了身体里。在我看来,这个版本显然是低劣的,因为它所做的只是为读者创造额外的工作。

到目前为止,我认为你所遇到的问题是你的声明性推理与Prolog的计算引擎密切相关。像我提供的更纯粹的声明性读取更简单,看起来更像是Prolog所说的(并且与它的评估方式关系不大)。

你需要练习将这些概念分开,但我认为它会帮助你变得更好(显然这是你关心的事情)。与此同时,来到这里寻求帮助并没有错,就像你在困惑时一直在做的那样。 :)

让我知道我是否可以提供更多帮助!

答案 1 :(得分:2)

当你试图找出谓词的声明性含义时,你会问:这个谓词适用于哪些解决方案?

Prolog的 1 子句独立地为这组解决方案做出贡献。因此,在条款之间建立任何联系都需要一些额外的审查。很容易做出一些不是这样的假设:

  

myappend1([],L,L).   如果 it 这个事实不正确,则意味着第一个列表不为空,所以...

考虑目标,myappend1([],[],[a]).事实不适用,第一个列表仍为空。在这里,您试图实现该子句的含义。这样做非常诱人,因为编程语言的大部分只能通过想象逐步发生的事情来理解。 Prolog的难点在于试图忽略这些细节,而不是完全忽略程序方面。

  

myappend1([X|L1],L2,[X|L3]) :- myappend1(L1,L2,L3).

要阅读规则,特别是递归规则,查看:-这是一个1970年代的←渲染是有帮助的。所以它是一个箭头,但它从右到左。因此,您可以从右侧开始阅读以下规则:

如果myappend(L1,L2,L3)成立,现在越过:-越过左侧myappend([X|L1],L2,[X|L3])成立。

有时,更好的方法是阅读这样的规则是完全覆盖头部并询问

  

??? :- myappend1(L1,L2,L3).

假设,我知道一些L1L2L3等待myappend1(L1,L2,L3).我能从中得出什么结论?有什么有趣的吗?有什么相关的东西我可以轻松地从这3个值中构建出来吗?

这一开始有点令人反感,因为你可能会说:但我怎么知道存在这种情况呢?好吧,你没有。您只是假设它存在。如果它永远不会存在,那么你永远无法得出那个结论。

许多人试图从左到右阅读规则,但是Prolog实际上是从左到右执行它们,它们涵盖的含义更容易理解为结论的方向。当Prolog从左到右执行规则时,它不知道这是否会成功。因此执行可能完全是投机性的。想想append(L1,[z],[a,b,c,d,e])。在这里,Prolog将对列表的每个元素应用此规则。但所有这些应用都是徒劳的。也就是说,最终它会失败。

<小时/> 精细打印

1实际上,是Prolog的纯粹,单调的子集。