我正在努力进一步了解Prolog,以及它如何处理统一。在这种情况下,它如何处理与列表的统一。
这是我的知识库;
member(X, [X|_]).
member(X, [_|T]):- member(X, T).
如果我正确理解了这个过程。如果member(X, [X|_])
不成立,则会进入递归规则,如果X
位于列表T
中,则[_|T]
与T
统一。< / p>
那么我的递归谓词中的匿名变量会发生什么?它被丢弃了吗?我很难理解与列表的确切统一过程,因为[_|T]
是两个变量,而不是一个变量。我只想弄清楚统一过程如何与列表精确协作。
答案 0 :(得分:2)
假设_
为Y
member(X, [Y|T]):- member(X, T).
无论True
如何,这都是Y
。现在你正在“回归”member(X, T)
。换句话说,您正在放弃Y
和“返回”member(X, T).
_
表示无论如何,忽略该变量。
_就像任何其他变量一样,除了你看到的每个变量 作为一个不同的变量处理,Prolog不会告诉你它是什么 与...结合。那里没有特别的行为;如果它让你感到困惑 关于行为,只需发明一个全新的变量并把它 在那里看看它做了什么。
在您的情况下,您的函数检查列表中是否存在给定元素,因此,您获取列表的第一个元素,检查是否相等,如果不相等,则丢弃该元素并继续。
答案 1 :(得分:2)
我认为关于列表如何表示为变量的主要问题已得到充分回答,但我觉得Prolog还有其他一些方面需要澄清。
要理解Prolog的谓语和从句,不要将它们视为&#34;函数&#34;这是一个好主意。那&#34;返回&#34;事情,甚至比喻。它可以引导你走Prolog中命令式思维的黑暗道路。 :)
在考虑谓词时:
(1) member(X, [X|_]).
(2) member(X, [_|T]) :- member(X, T).
将member/2
视为 relation ,它描述了元素X
何时是列表L
的成员,并且子句是用于确定何时的规则这是真的。
我假设你已经知道如何根据其他答案在Prolog中表示列表(例如,Will Ness&#39;详细答案)。
第一个条款说:
(1) X
是[X|_]
的成员,无论列表[X|_]
的尾部是什么
在该表示法中,变量_
表示列表[X|_]
的尾部,X
表示该列表的第一个元素。很简单,X
是此列表的成员,因此member(X, [X|_]).
是事实。无论列表尾部是什么都是如此,因此我们只使用_
(匿名变量),因为此规则不需要信息。 Prolog在技术上并没有把价值扔掉#34;程序员因为程序员没有使用它而把它扔掉了。相反,如果我们说member(X, [X|T]).
可以正常工作,但我们不会使用T
。 Prolog可以实例化它,但它不会被使用。这就像在C中分配x = 3
但不使用它的价值一样。在这种情况下,Prolog会发出关于&#34;单身人士的警告。变量。注意那些,因为它通常意味着你拼写错误的东西或忘记了一些东西。 :)
下一个规则是递归的。它说:
(2) X
是列表[_|T]
的成员,如果 X
是尾部的成员(T
)该列表的内容,无论列表的第一个元素是什么
我们在这里考虑的不太重要的情况是列表中的第一个元素可能与X
不匹配,因此member(X, L)
的真值取决于此规则member(X, T)
的真值,其中T
是L
的尾部(除了第一个元素之外的所有元素)。规则不将member(X, [_|T])
与member(X, T)
统一,因此不会将T
与[_|T]
统一起来,因为您可能假设。 :-
运算符定义规则或含义(请注意规则说明中的 if )。 [ NB ,如果您统一这些条款,则可以使用统一运算符=/2
完成:{{ 1}}]。
在递归查询member(X, [_|T]) = member(X, T)
上,Prolog回到顶部,即第一个规则,并尝试将第一个参数member(X, T)
与第二个参数的头部统一(这是原始列表减去它的第一个元素,或 head ),如果它不匹配,再次进入规则#2,不断检查尾部,直到它可以统一。如果它到达尾部为空(X
)并且无法将[]
与任何元素统一起来,则谓词将失败,因为没有匹配的事实或规则{ {1}}。但是,如果它确实将X
与元素统一,则成功(不&#34;在某个函数意义上返回值*在其他语言中)并显示它在进程中的参数中实例化的任何变量的值,或者只是在传递的所有参数都已实例化时才会成功。如果成功后需要检查更多规则(有什么称为选择点),它会(如果你告诉它)返回并检查更多解决方案,如果是找到它们,也显示它们。如果没有,请显示member(X, [])
或X
。
查看示例查询, no
是false
的成员吗?
b
Prolog将首先尝试使用谓词的 fact 或 head 来统一查询。它找到的第一个是:
[a,b,c]
在尝试统一时,member(b, [a,b,c]).
,但member(X, [X|_]).
(或,X = b
在头尾符号中)并不与[b | _] {{1 } [a,b,c]
b`不匹配)。 Prolog然后转到下一个条款:
[a|[b,c]]
在(note the head elements
与头部统一时,它会出现:
and
它现在有追逐的递归查询:member(X, [_|T]) :- member(X, T).
。由于它是一个新查询,它会再次从顶部开始,并尝试将其与member(b, [a,b,c])
统一起来。现在,它取得了成功,因为member(b, [_|[b,c]]) :- member(b, [b,c]).
(或member(b, [b,c])
)符合此模式:member(X, [X|_])
。
因此,member(b, [b,c])
成功,Prolog将返回&#34; true&#34;。但是,它还没有完成,因为它留下了所谓的选择点。即使它与member(b, [b|[c]])
匹配第一个条款,它仍然希望返回并找到更多使其成功的案例,并且还有另一个条款要追求。因此,Prolog将返回并针对第二个子句尝试member(b, [b|_])
,将member(b, [a,b,c]).
与member(b, [b,c])
匹配并执行另一个递归查询member(b, [b,c])
,依此类推,直到最终无法找到另一种方案。这就是查询看起来像这样的原因:
member(b, [b|[c]])
它首先成功,但随后我们要求更多(使用member(b, [_|[c]])
)然后失败(member(b, [c])
)。这让一些Prolog初学者感到困惑,但这是因为我们已经要求Prolog获得另一种解决方案,而且它说没有。
因为Prolog继续尝试寻找解决方案(根据要求),您还可以在查询中使用变量:
| ?- member(b, [a,b,c]).
true ? ;
no
| ?-
此查询的运行方式与前一个示例相同,但现在我将变量作为第一个参数。 Prolog会将此成功与第一个条款匹配:;
通过no
与member(E, [a,b,c]).
统一。所以你会看到类似的东西:
member(E, [a,b,c])
如果我们现在要求使用member(X, [X|_])
提供更多解决方案,Prolog会回过头来尝试第二个条款,将E = a
与| ?- member(E, [a,b,c]).
E = a ?
统一;
(在此处忽略谓词)和member(E, [a|[b,c]])
。然后递归查询member(X, [_|T])
,并且由于它是新查询,因此返回到顶部并再次匹配_ = a
,这次是T = [b,c]
。所以我们看到:
member(E, [b,c])
等等。 member(X, [X|_])
会找到E = b
使| ?- member(E, [a,b,c]).
E = a ? ;
E = b ?
为真的所有值,然后在耗尽member(E, [a,b,c])
的所有元素后最终失败。
答案 2 :(得分:1)
列表只是'.'
仿函数的复合词:
1 ?- [_|T] = .(_,T).
true.
2 ?- [_|T] =.. X.
X = ['.', _G2393, T].
复合词的结构统一的通常过程适用:
3 ?- [A|T] = .(B,R).
A = B,
T = R.
[A|T]
实际上是.(A,T)
所以仿函数(.
)和arities(两个术语都是二元,arity 2)匹配,所以各个成分也匹配。< / p>
是的,为了报告统一结果,将忽略匿名变量_
。否则它只是一个新的唯一命名变量。
它进入递归规则,如果X在列表T中,则[_ | T]与T统一。
不完全。统一发生在之前&#34;继续&#34;,作为子句选择的一部分。要将列表L
统一到[_|T]
,请选择其尾部&#34;并T
引用它。 E.g。
4 ?- L = [1,2,3], L = [_|T].
L = [1, 2, 3],
T = [2, 3].
(_
为1
,但未报告。)
答案 3 :(得分:1)
[A|B]
表示一个列表,其中A是Head元素,B是整个剩余列表。
所以很快就解释一下这个算法:
将匿名变量_
想象为您以后不需要的变量。如果您用大写字母替换_
,该算法也会起作用,但它会给您一个警告,指出您从未使用过的变量。