在阅读时钟,Mellish的 Prolog编程(2003)时出现了问题。
假设我们使用列表从一个句子映射到另一个句子
change(keith, he).
change(very, too).
change(X, X).
alter([], []).
alter([H|T], [X|Y]) :- change(H, X), alter(T, Y).
有人可以阐明变量是如何实例化的吗?我的书并没有特别深入。
例如,使用跟踪时:
Call: (7) alter([keith, is, very, cool], _G2676) ? creep
Call: (8) change(keith, _G2756) ? creep
Exit: (8) change(keith, he) ? creep
Call: (8) alter([is, very, cool], _G2757) ? creep
Call: (9) change(is, _G2759) ? creep
Exit: (9) change(is, is) ? creep
Call: (9) alter([very, cool], _G2760) ? creep
Call: (10) change(very, _G2762) ? creep
Exit: (10) change(very, too) ? creep
Call: (10) alter([cool], _G2763) ? creep
Call: (11) change(cool, _G2765) ? creep
Exit: (11) change(cool, cool) ? creep
Call: (11) alter([], _G2766) ? creep
Exit: (11) alter([], []) ? creep
Exit: (10) alter([cool], [cool]) ? creep
Exit: (9) alter([very, cool], [too, cool]) ? creep
Exit: (8) alter([is, very, cool], [is, too, cool]) ? creep
Exit: (7) alter([keith, is, very, cool], [he, is, too, cool]) ? creep
B = [he, is, too, cool] .
我看到打开了几个内存地址引用,就像在规则中遇到alter时一样,并递归配对为:
alter([is, very, cool], ???? tail of Z
看起来新Y将取Z的尾部值,但由于Z只是对内存中某个点的引用,这些变量在返回之前如何赋值?同样源于这种混淆的是,我无法清楚地表明变更谓词中尾部X中的值如何能够“建立”,从而保留变更谓词中的新值。 。
答案 0 :(得分:2)
你带来了很多程序性的想法("行李")来阅读这条痕迹。让我们尝试用Prolog的眼睛阅读它:
Call: (7) alter([keith, is, very, cool], _G2676) ? creep
Prolog只是在这里重申你的目标。您可以忽略生成的变量_G2676
中的数字;它不是内存位置,生成这些变量名称的方法因实现和平台而异,Prolog中没有机制来读取任意内存位置,这些生成的变量数字对你来说几乎无法访问每一种感觉。它只是一个内部记账的东西。
你可能发出了这样的查询:
?- alter([keith, is, very, cool], B).
这样想。你问Prolog,"证明alter([keith, is, very, cool], B)
并告诉我B是什么。"所以它会尝试去做。
Call: (8) change(keith, _G2756) ? creep
这里发生了什么? Prolog查看了您的查询,发现您正在使用alter/2
。它将alter/2
的 head 替换为alter/2
的 body 。快速Prolog解剖课:
alter([H|T], [X|Y]) :- change(H, X), alter(T, Y).
------------------- --------------------------
"the head" "the body"
所以Prolog说,"要证明alter([keith,is,very,cool], B)
我必须用alter([H|T], [X|Y])
= [keith,is,very,cool]
和[H|T]
= B
证明[X|Y]
。 (Prolog以合理的方式管理这些变量名。)所以现在Prolog用change(H, X), alter(T, Y)
替换活动查询。除外,[H|T]
= [keith|[is,very,cool]]
和B
= [X|Y]
。因此,在跟踪中打印的内容是:
Call: (8) change(keith, _G2756) ? creep
现在Prolog必须查看它的第一个change/2
子句,并查找change(keith, he)
。所以Prolog说,"啊哈! X = he
"然后打印:
Exit: (8) change(keith, he) ? creep
Prolog现在说,"好吧,我只需要通过查看alter/2
正文中的下一步来完成我在一秒钟前开始的查询,这是:
Call: (8) alter([is, very, cool], _G2757) ? creep
好吧,就像以前一样,Prolog现在要用alter/2
的主体替换alter/2
查询并尝试实现它,这意味着它现在必须证明:
Call: (9) change(is, _G2759) ? creep
这个稍微有点儿,因为change/2
的第一个条款不匹配。 Prolog无法将keith
与is
统一起来,因此它会失败并转到第二个子句。它无法将is
与very
统一起来,因此会转到第三个子句并将is
与自身统一起来:
Exit: (9) change(is, is) ? creep
这个过程重复,处理非常/太酷和冷却,然后Prolog用完列表成分,然后输入alter/2
的基本情况:
Call: (11) alter([], _G2766) ? creep
Exit: (11) alter([], []) ? creep
如果您需要注意,您应该注意到带有匿名变量的查询行开始Call:
表示Prolog正在更改问题,当Prolog回答问题时,该行以{开头{1}}。还有一些其他的东西可以开始告诉你关于Prolog的执行模型的其他事情:Exit:
和Retry:
。但是你没有在这里,因为你的查询实际上是第一次尝试。
从这里开始,你得到Fail:
,因为一切都已成功统一,Prolog基本完成。但这就是"建立"正在发生:
Exit
这里发生的事情是Exit: (11) alter([], []) ? creep
Exit: (10) alter([cool], [cool]) ? creep
Exit: (9) alter([very, cool], [too, cool]) ? creep
Exit: (8) alter([is, very, cool], [is, too, cool]) ? creep
Exit: (7) alter([keith, is, very, cool], [he, is, too, cool]) ? creep
被证明了,所以它返回到外部调用(注意那里的数字;它们告诉你关于调用堆栈),这意味着Prolog现在已经证明了alter([], [])
为真,这意味着它已被证明alter([cool], [cool])
是真的,等等。这只是尾递归的工作方式。最后,它输出您的查询成功:
alter([very, cool], [too, cool])
所以,就像你说的那样,这里真的没有打开内存地址。
看起来新的Y会取Z的尾部值,但由于Z只是对内存中某个点的引用,这些变量在返回之前如何赋值?
在"正常"在编程语言中,您可以使用某些值调用函数,并将一个值作为返回值返回。 Prolog不是一种普通的编程语言;你没有定义函数,你正在定义关系。关系比函数更少受约束:有时您的参数是输入,有时它们是输出。 Prolog中的变量不是"对内存中斑点的引用。"它们只是数据的名称;它们可以 ground 或 free ,具体取决于它们是否已被弄清楚。
Prolog中的每个评估步骤,Prolog都在搜索自由变量的绑定。这称为统一,是Prolog中的基本执行模型。当您发出B = [he, is, too, cool] .
之类的查询时,您要求Prolog证明change(keith, X)
为真,并为您提供值为X的值。 Prolog会看一下,然后回来change(keith, X)
。但你也可以问它true, X = he
,Prolog会查看它并说change(X, he)
。这是使它成为一种关系而不是一种功能的一部分。你也可以说true, X = keith
它会以change(X, Y)
返回,而且多种结果是你知道你处理关系而不是函数的另一种方式。
所以在Prolog中,变量不是内存中的一个位置",它是名称和值之间的绑定,Prolog能够在评估的任何步骤建立绑定,而不仅仅是在计算中或计算过程中,但在成功计算的路上也是如此!
我敢打赌,如果你回顾一下你的书,你会发现它会详细介绍这一点。它是那里最好的Prolog书之一,它肯定会详细介绍这些东西。但我希望无论如何这有帮助!