我以为我了解在 Scala 和 Haskell 中发现的模式匹配与在 Prolog 中发现的统一性有何不同,但我对 Prolog 的误解很大。有哪些简单的问题可以由一个人解决而另一个人无法解决?谢谢
4 回答
简单的说法:模式匹配是单向的,统一是双向的。也就是说,在 Prolog 中,右侧(被匹配的那个)可以包含未绑定的变量。例如,如果您有两个未绑定的变量X
和Y
,这将正常工作:
X = Y,
X = 5,
%% Y = 5 now as well
在 Erlang(使用接近 Prolog 语法的模式匹配)中,该行将X = Y
产生错误:variable 'Y' is unbound
. 请注意,X
未绑定很好:它应该是模式匹配的。
当您想要处理部分定义的值时,这可能很有用。一个很好的例子是差异列表。
这也是允许在多种模式下使用 Prolog 谓词的原因。例如,在 Scala/Haskell/Erlang 中,如果你想 1) find A ++ B
,2) 求解方程A ++ X == B
,或 3) 求解X ++ A == B
给定列表的方程A
和B
,你需要编写 3 个单独的函数;在 Prolog 中,所有这些工作(以及更多!)都由一个谓词完成。
我认为将概念形式化而不是研究特定的语言是有用的。匹配和统一是比模式匹配和序言在更多上下文中使用的基本概念。
- 项 s 匹配 t,当且仅当存在替换 phi 使得 phi(s) = t
- 项 s 与项 t 合一,当且仅当存在 phi(s) = phi(t)
举个例子,我们检查术语 s = f(Y,a) 和 t = f(a,X) 其中 X,Y 是变量,a 是常数。s 不匹配 t,因为我们不能普遍量化常数 a。但是,s 和 t 有一个统一符:phi = {X\a, Y\a}
Scala 中的以下内容将无法编译,因为它的第一种情况分支尝试x
两次声明变量。
(1, 1) match{
case (x, x) => println("equals")
case _ => println("not equals")
}
如果 Scala 使用统一而不是模式匹配,这将成功并打印“等于”,而
(1, 2) match{
case (x, x) => println("equals")
case _ => println("not equals")
}
将打印“不等于”。这是因为当尝试将变量绑定x
到1
和时,统一会失败2
。
在 Prolog 中,您可以像这样将 [3] 附加到 [1,2] :
?- append([1,2], [3], Z).
Z = [1, 2, 3].
统一的巧妙之处在于您可以使用相同的代码('append' 的内部定义),而是找到从第一个参数获取结果所需的第二个参数:
?- append([1,2], Y, [1,2,3]).
Y = [3].
不是通过编写“做这个,然后做那个”来编码,而是通过说出你所知道的来在 Prolog 中编码。Prolog 将您提供的事实视为方程式。统一让它采用这些方程并求解您还不知道其值的任何变量,无论它们是在右侧还是左侧。
因此,例如,您可以在 Prolog 中编写一个计划器,然后您可以“向前”运行它,给它一个计划并让它预测其结果;或者你可以“向后”运行它,给它一组结果并让它构建一个计划。你甚至可以同时以两种方式运行它(如果你在编码时小心的话),指定一组目标和一组对计划的约束,这样你就可以说“找到一个不涉及工作的计划坐地铁。”