我遇到过这段代码:
connectRow(_,_,0).
connectRow([spot(_,R,_,_)|Spots],R,K) :- K1 is K-1, connectRow(Spots,R,K1).
/*c*/
connectRows([]).
connectRows(Spots) :-
connectRow(Spots,_,9),
skip(Spots,9,Spots1),
connectRows(Spots1).
connectRow(Spots,_,9)中的通配符如何工作?它如何知道要检查哪些值以及它如何知道它检查了所有可能的值?
编辑:我想我理解为什么会有效,但如果有人可以为我验证这一点我会喜欢它: 当我使用通配符“调用”connectRow时,它会将通配符与connectRow谓词中的“R”匹配。这可能是吗?
答案 0 :(得分:2)
_
就像任何其他变量一样,除了你看到的每个变量都被视为一个不同的变量,而Prolog不会告诉你它与它的结合。那里没有特别的行为;如果它让你对这个行为感到困惑,那就发明一个全新的变量并把它放在那里看看它的作用。
让我们谈谈Prolog如何处理变量。如果您碰巧拥有它们,那么您可以遵循的实验应该破坏无益的先入为主的观念。
?- length([2,17,4], X)
X = 3.
很多Prolog看起来像这样,很容易陷入认为有指定" out"变量与返回值一样工作并指定" in"像参数一样工作的变量。毕竟:
?- length([2,17,4], 3).
true.
?- length([2,17,4], 5).
false.
在这里,我们开始看到有趣的事情正在发生。一个错误的直觉是Prolog以某种方式跟踪输入和输出变量并且"检查"在这种情况下。但这不是发生了什么,因为统一比这更普遍。观察:
?- length(X, 3).
X = [_G2184, _G2187, _G2190].
我们现在已经把传统的参数/返回值改为:Prolog知道X是一个长三项的列表,但不知道这些项目究竟是什么。信不信由你,这种技术常常用于生成变量,当你知道你需要多少变量但你不需要单独命名它们时。
?- length(X, Y).
X = [],
Y = 0 ;
X = [_G2196],
Y = 1 ;
X = [_G2196, _G2199],
Y = 2 ;
X = [_G2196, _G2199, _G2202],
Y = 3
恰好长度的定义非常通用,Prolog可以使用它来生成列表及其长度。这种行为是Prolog如此擅长生成和测试的一部分。解决方案。您可以逻辑地定义问题,Prolog应该能够生成逻辑上合理的值来进行测试。
所有这些变化都源于一个非常简单的长度定义:
length([], 0).
length([_|Rest], N1) :-
length(Rest, N0),
succ(N0, N1).
关键是不要像计算长度的过程那样读取它,而是将其视为列表和数字之间的逻辑关系。该定义是归纳的,将空列表与0相关联,并将包含一些项目的列表与1 +列表其余部分的长度相关联。使这项工作的引擎称为统一。
在第一种情况下,length([2,17,4], X)
,值[17,4]与Rest统一,N0与2统一,N1与3统一。该过程是递归的。在最后一种情况下,X与[]和Y统一为0,这自然导致下一个我们有一些项目并且Y为1的情况,以及表示列表中的项目的变量没有这样的事实有什么特别的东西可以统一起来并不重要因为从来没有使用过这个变量的价值。
看看你的问题,我们看到了同样的递归结构。这些谓词非常复杂,所以让我们把它们分成几部分。
connectRow(_, _, 0).
这表示connectRow(X, Y, 0)
为真,无论X和Y如何。这是基本情况。
connectRow([spot(_, R, _, _)|Spots], R, K) :-
此规则匹配特定结构的点列表,假设第一个点的第二个值(R)与第二个参数匹配。
K1 is K-1, connectRow(Spots, R, K1).
本节的主体基本上是在递减K,即第三个参数。
现在很清楚,这基本上会产生一个类似于[spot(_, R, _, _), spot(_, R, _, _), ... spot(_, R, _, _)]
且长度为K且在spot
的其他三个位置没有特定值的列表。确实是我们在测试时看到的:
?- connectRow(X, Y, 0).
true ;
(infinite loop)^CAction (h for help) ? abort
% Execution Aborted
?- connectRow(X, Y, 2).
X = [spot(_G906, Y, _G908, _G909), spot(_G914, Y, _G916, _G917)|_G912] ;
(infinite loop)^CAction (h for help) ? abort
所以这里似乎有一些错误;如果我确定这些是我要说的全部故事:
clpfd
进行更改我们会得到略微不同的行为:
:- use_module(library(clpfd)).
connectRow([], _, 0).
connectRow([spot(_, R, _, _)|Spots], R, K) :-
K #> 0, K1 #= K-1, connectRow(Spots, R, K1).
?- connectRow(X, Y, 0).
X = [] ;
false.
?- connectRow(X, Y, 1).
X = [spot(_G906, Y, _G908, _G909)] ;
false.
?- connectRow(X, Y, Z).
X = [],
Z = 0 ;
X = [spot(_G918, Y, _G920, _G921)],
Z = 1 ;
X = [spot(_G918, Y, _G920, _G921), spot(_G1218, Y, _G1220, _G1221)],
Z = 2
你会注意到,在结果中我们有Y站在我们的spot
结构中,但我们在其他位置看到自动生成的变量很奇怪,例如_G918
。碰巧的是,我们可以使用_
代替Y并看到类似的效果:
?- connectRow(X, _, Z).
X = [],
Z = 0 ;
X = [spot(_G1269, _G1184, _G1271, _G1272)],
Z = 1 ;
X = [spot(_G1269, _G1184, _G1271, _G1272), spot(_G1561, _G1184, _G1563, _G1564)],
Z = 2
所有这些看起来很奇怪的变量都存在,因为我们使用了_
。请注意,所有spot
结构在第二个位置都具有完全相同的生成变量,因为Prolog被告知必须将connectRow
的第二个参数与spot
的第二个位置统一起来。它无处不在,因为R是"传递"下一次调用connectRow,递归。
希望这有助于解释您示例中_
的内容,以及一般的Prolog统一。
修改:用R
统一内容要回答下面的问题,您可以直接将R与值统一,或者将R绑定到变量并使用变量。例如,我们可以直接绑定它:
?- connectRow(X, 'Hello, world!', 2).
X = [spot(_G275, 'Hello, world!', _G277, _G278), spot(_G289, 'Hello, world!', _G291, _G292)]
我们也可以绑定它,然后再分配它:
?- connectRow(X, R, 2), R='Neato'.
X = [spot(_G21, 'Neato', _G23, _G24), spot(_G29, 'Neato', _G31, _G32)],
R = 'Neato'
说R=<foo>
没什么特别的;它统一了表达式的两面,但是双方都可以是表达式而不是变量:
?- V = [2,3], [X,Y,Z] = [1|V].
V = [2, 3],
X = 1,
Y = 2,
Z = 3.
所以你也可以在另一个谓词中使用R:
?- connectRow(X, R, 2), append([1,2], [3,4], R).
X = [spot(_G33, [1, 2, 3, 4], _G35, _G36), spot(_G41, [1, 2, 3, 4], _G43, _G44)],
R = [1, 2, 3, 4] ;
请注意,这为回溯和生成其他解决方案创造了机会。例如:
?- connectRow(X, R, 2), length(R, _).
X = [spot(_G22, [], _G24, _G25), spot(_G30, [], _G32, _G33)],
R = [] ;
X = [spot(_G22, [_G35], _G24, _G25), spot(_G30, [_G35], _G32, _G33)],
R = [_G35] ;
X = [spot(_G22, [_G35, _G38], _G24, _G25), spot(_G30, [_G35, _G38], _G32, _G33)],
R = [_G35, _G38] ;
希望这有帮助!