我试图学习序幕,但是我遇到了麻烦。
我在下面有一个例子以及它输出的内容,我清楚地坚持一些概念,但不确定是什么。
output([]).
output([c|R]):- output(R), !, nl.
output([X|R]) :- output(R), write(X).
?- output([a,b,c,d,e]).
答案:
ed
ba
true.
如果我错了,请纠正我,但到目前为止,这是我所理解的......
当我们致电output([a,b,c,d,e])
时。
prologue使用统一寻找解决方案,
它尝试output([])
并失败,因此它继续进行第二个output([c|R])
,然后递归地将列表的尾部传递到output([c|R])
,直到它遇到{{1}的基本情况}。
现在我感到困惑......然后点击了将output([])
锁定到R
和[]
的值为c
的剪辑?之后的输出如何发生?我真的很困惑。
答案 0 :(得分:0)
你的推理的主要问题是c
不是变量而是原子。它不能与任何其他值统一。
因此,对于您的示例输入,对于前2个调用,它将不会执行output([c|R])
(因为a
和b
可以与c
统一,但它会改为output([X|R])
。仅对于第三次调用,当头为c
时,调用前一个子句。在此之后,它会为d
和e
再次调用后一个子句2次,然后它会触及基本情况。
从那时起,我们可以很容易地看到输出:如果先写入'然后' d',那么换一行(我们匹配的时间{{1} }),广告然后c
和b
。最后得到a
作为输出,表明谓词调用成功。
另请注意,由于剪切,我们只获得一个输出。如果没有剪切,我们也会得到true
,因为edcba
案例也可以匹配最后一个句子。
答案 1 :(得分:0)
我认为你对Prolog正在做什么以及统一是什么有着根本的误解。在Prolog中,当您进行output([a,b,c,d,e]).
之类的查询时,Prolog将从您断言的事实和谓词的开头开始,并尝试统一这个术语(您的查询)与事实或头部谓词。
<强>统强>
我们需要暂时停下来了解统一是什么。在Prolog中,运算符=/2
是统一运算符,可用于查询两个术语term1 = term2
的统一。如果term
和term2
可以成功统一,则此查询将成功。他们如何成功统一?如果term1
和term2
中存在变量的绑定,就会发生这种情况,这样这些术语基本上是相同的(通过&#34;本质上是#34;我的意思是它们可能仅仅在句法表示方面有所不同但是在规范形式时真正相同 - 请参阅下面的详细信息。
以下是失败的统一尝试示例。您可以在Prolog提示符下输入这些内容,它会立即显示失败。
a = e. % This fails because the atom `a` is different than the atom `e1`
% There are no variables here that can change this fact
foo(X) = bar(Y)
% This fails because the functor `foo` is different than
% the functor `bar`. There's no way to get these terms to match
% regardless of how the variables `X` or `Y` might be instantiated
foo(a, Y) = foo(b, Y)
% This fails because no matter how the variable `Y` is instantiated
% the 1st argument of `foo` just cannot match. That is, the atom
% `a` doesn't match the atom `b`.
foo(a, b, X) = foo(a, b)
% This fails because the `foo/3` and `foo/2` have a different
% number of arguments. No instantiation of the variable `X` can
% change that fact
[1,2] = [1,2,3] % Fails because a list of 2 elements cannot match a list of 3 elements
[] = [_|_] % Fails because the empty list cannot match a list of at
% least one element.
[a,b,c] = [x|T] % Fails, regardless of how `T` might be bound, because `[a,b,c]`
% is a list whose first element is `a`
% and `[x|T]` is a list whose first element is `x`. The
% atoms `a` and `x` do not and cannot match.
以下是成功统一的例子。您也可以在Prolog提示符下测试这些,并且您应该获得成功,或者,如果涉及变量,则至少获得一个解决方案,显示导致其成功的变量的绑定:
a = a. % Trivial case: an atom successfully unifies with itself
X = a. % Succeeds with `X` bound to `a`
foo(X) = foo(a). % Succeeds with `X` bound to `a`
[a,b,c] = [a|T] % Succeeds with `T` bound to `[b,c]` because the first element
% `a` is the same in both cases.
[1,2,3] = [H|T] % Succeeds with `H` bound to 1, and `T` bound to `[2,3]`
% since `[1,2,3]` is equivalent to `[1|[2,3]]` (they are two
% different syntaxes representing the same term)
暂且不说:Prolog列表语法
我们正在使用其他语言熟悉的表单撰写列表。因此[]
是一个空列表,[1,2,3]
是3个元素1,2和3的列表。您还可以在列表中包含列表,或列表中的任何条目。例如,这是一个包含3个元素的有效列表:[a, [1,foo(a)], bar(x,Y,[])]
。第一个元素是a
,第二个元素是两个元素的列表[1, foo(a)]
,第三个元素是bar(x,Y,[])
。在Prolog中,您还可以使用描述一个或多个元素中的第一个和尾部的形式编写列表。例如,[H|T]
是一个列表,其第一个元素是H
,列表的其余部分是T
(本身就是一个列表)。至少有两个元素的列表可以写为[H|T]
,并且您知道T
至少有一个元素。或者您可以将其写为[H1,H2|T]
并明确指出前两个元素并理解T
将是零个或多个参数的列表。第一个元素是列表的各个元素,尾部是表示列表的 rest 的列表。以下表格均代表[a,b,c,d,e]
列表:
[a,b,c,d,e]
[a|[b,c,d,e]]
[a,b|[c,d,e]]
[a,b,c|[d,e]]
[a,b,c,d|[e]]
[a,b,c,d,e|[]]
如果您有一个列表L
,并希望prolog确保L
至少有两个参数,您可以将L
与2个元素的匿名列表统一起来:{{ 1}}。只有L = [_,_|_]
是至少两个元素的列表时才会成功。
另一方面:规范形式
但是,Prolog具有所谓的规范形式用于术语,它是给定术语的基本表示。您可以通过调用L
:来查看术语的规范形式
write_canonical(Term)
这很有趣,究竟是什么?它根本不像一个列表!它实际上是Prolog中的规范形式,对于Prolog来说真正的列表(如果你想这样想的话)。 Prolog中的基本术语形式是仿函数和零个或多个参数。原子| ?- write_canonical([a,b,c]).
'.'(a,'.'(b,'.'(c,[])))
yes
是一个可以被视为没有参数的仿函数a
的术语。术语a
包含仿函数foo(1,X)
以及参数1和foo
。以这种方式编写的列表X
只是程序员的一种方便的语法,使其易于阅读。一个列表实际上是由仿函数[a,b,c]
和两个参数组成的:头部和尾部。所以列表'.'
一般是[H|T]
,空列表'.'(H,T)
就是它自己,一个代表空列表的原子。当Prolog统一(或尝试统一)两个列表时,它真正将列表视为[]
,因此它与'.'(H, T)
仿函数匹配,然后尝试匹配参数。在多个元素的情况下,它是一个递归匹配,因为'.'
本身就是一个列表。
T
等Prolog中的表达式也是规范形式{@ 1}}的语法便利。
回到我们的故事
正如我们所说,当您查询X + 3
时,Prolog会尝试将此与谓词条款或您已声明的事实的头部统一起来。以下是您所声称的内容:
'+'(X, 3)
从顶部开始,Prolog尝试这种统一:
output([a,b,c,d,e]).
由于没有变量可以更改条款以使它们匹配,因此失败。它失败,因为列表output([]).
output([c|R]):- output(R), !, nl.
output([X|R]) :- output(R), write(X).
和空列表output([a,b,c,d,e]) = output([])
无法匹配。
转到下一条:
[a,b,c,d,e]
只有统一[]
能够成功绑定output([a,b,c,d,e]) = output([c|R])
时才能成功。您可以将其视为[a,b,c,d,e] = [c|R]
。显然,要使这种统一成功,每个清单的第一个要素必须匹配。但R
和[a|[b,c,d,e,]] = [c|R]
不匹配,因此失败。
到下一个:
a
Prolog尝试将c
与output([a,b,c,d,e]) = output([X|R])
统一,或[a,b,c,d,e]
与[X|R]
统一...此后自[a|[b,c,d,e]]
和[X|R]
成功是变量,它们可以绑定为X
和R
。现在可以执行该子句的主体:
X = a
在我们转到R = [b,c,d,e]
之前,呼叫output([b,c,d,e]), write(a).
必须先执行并成功。遵循上述相同的逻辑, write(a)
谓词的第一个和第二个子句与不匹配。但是第3个子句再次与output([b,c,d,e])
匹配,导致output/1
和[b,c,d,e] = [X|R]
。现在再次执行此子句的主体(并且必须记住我们现在在递归调用中已经深入一级...上面对X = b
的调用正在等待等待结果):
R = [c,d,e]
现在它变得更有趣了。由于output([b,c,d,e])
失败,output([c,d,e]), write(b).
的第一个句子仍然不匹配。但是第二个子句现在匹配,因为output/1
成功绑定[c,d,e] = []
。所以该身体被执行:
[c,d,e] = [c|R]
现在我们需要追逐对R = [d,e]
的调用(我们现在要记住递归的另一个级别!)。这个与前两个子句匹配,但匹配第3个子句,output([d,e]), !, nl.
与绑定output([d,e])
和[d,e] = [X|R]
匹配。
我可以继续前进,但是我已经厌倦了打字,而且我确实有一份工作,而且我已经没时间了。你应该听到这个想法,然后自己开始研究这个逻辑。向前推进的一个重要提示是,当你最终在递归调用中找到X = d
并匹配第一个子句时,你将开始&#34;展开&#34;递归调用(如果你手动执行此操作需要跟踪),R = [e]
调用将开始执行以及第二个子句的output([])
部分将write(X)
作为第一个元素匹配的情况。
玩得开心......