我有一个非常简单的问题:编写一个Prolog程序来实现附加的Prolog函数,该函数连接两个字符串并按以下方式工作:
所以我有以下两种解决方案,我不确定我的陈述性解释是否正确:
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,否则返回
我认为这条规则的陈述含义是:如果规则的主体是真的,则规则的头部是真的。
当它到达我已经证明事实的基本情况 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:
这是对的吗?
对我而言,以陈述的方式推理是如此困难: - (
答案 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).
假设,我知道一些L1
,L2
,L3
等待myappend1(L1,L2,L3).
我能从中得出什么结论?有什么有趣的吗?有什么相关的东西我可以轻松地从这3个值中构建出来吗?
这一开始有点令人反感,因为你可能会说:但我怎么知道存在这种情况呢?好吧,你没有。您只是假设它存在。如果它永远不会存在,那么你永远无法得出那个结论。
许多人试图从左到右阅读规则,但是Prolog实际上是从左到右执行它们,它们涵盖的含义更容易理解为结论的方向。当Prolog从左到右执行规则时,它不知道这是否会成功。因此执行可能完全是投机性的。想想append(L1,[z],[a,b,c,d,e])
。在这里,Prolog将对列表的每个元素应用此规则。但所有这些应用都是徒劳的。也就是说,最终它会失败。
<小时/> 精细打印
1实际上,是Prolog的纯粹,单调的子集。