SICP统一算法中看似不必要的情况
我试图理解 SICP 此处
特别是,在“extend-if-possible”过程中,有一个检查(第一个标有星号“*”的地方),用于检查右侧“表达式”是否为已绑定到当前帧中某些内容的变量:
(define (extend-if-possible var val frame)
(let ((binding (binding-in-frame var frame)))
(cond (binding
(unify-match
(binding-value binding) val frame))
((var? val) ; *** why do we need this?
(let ((binding (binding-in-frame val frame)))
(if binding
(unify-match
var (binding-value binding) frame)
(extend var val frame))))
((depends-on? val var frame)
'failed)
(else (extend var val frame)))))
相关注释指出:
“在第一种情况下,如果我们尝试匹配的变量未绑定,但我们尝试匹配它的值本身是一个 ( different) 变量,有必要检查该值是否已绑定,如果是,则匹配其值。如果匹配双方都未绑定,则我们可以将其中一个绑定到另一个。”
但是,我想不出实际上有必要这样做的情况。
我认为正在谈论的是,当前可能存在以下帧绑定:
{?y = 4}
然后被要求将绑定从 ?z “extendIfPossible” 到 ?y。
有了“*”检查,当要求用“?y”扩展“?z”时,我们看到“?y”已经绑定到4,然后递归地尝试将“?z”与“4”统一,这导致我们用“?z = 4”扩展框架。
如果没有检查,我们最终将仅用“?z = ?y”来扩展框架。 但在这两种情况下,只要 ?z 尚未绑定到其他东西,我就看不到问题。
注意,如果 ?z had 已经绑定到其他东西,那么代码不会到达标记为“*”的部分(我们已经递归到与 ?z 已经匹配的内容统一) 。
经过深思熟虑,我意识到生成“最简单”MGU(最通用统一器)可能存在某种争论。 例如,您可能想要一个具有最少数量的变量引用其他变量的 MGU...也就是说,我们宁愿生成替换 {?x = 4, ?y = 4} 而不是替换 {?x = ?y, ?y = 4}...但我不认为这个算法在任何情况下都能保证这种行为,因为如果你要求它统一“(?x 4)”和“(?y ?y)”那么你会最终仍然是 {?x = ?y, ?y = 4}。 如果行为不能得到保证,为什么还要增加复杂性呢?
我这里的推理都正确吗? 如果不是,需要“*”检查才能生成正确 MGU 的反例是什么?
I'm trying to understand the unification algorithm described in SICP here
In particular, in the procedure "extend-if-possible", there's a check (the first place marked with asterix "*") which is checking to see if the right hand "expression" is a variable that is already bound to something in the current frame:
(define (extend-if-possible var val frame)
(let ((binding (binding-in-frame var frame)))
(cond (binding
(unify-match
(binding-value binding) val frame))
((var? val) ; *** why do we need this?
(let ((binding (binding-in-frame val frame)))
(if binding
(unify-match
var (binding-value binding) frame)
(extend var val frame))))
((depends-on? val var frame)
'failed)
(else (extend var val frame)))))
The associated commentary states:
"In the first case, if the variable we are trying to match is not bound, but the value we are trying to match it with is itself a (different) variable, it is necessary to check to see if the value is bound, and if so, to match its value. If both parties to the match are unbound, we may bind either to the other."
However, I cannot think of a case where this is actually necessary.
What's it's talking about, I think, is where you might have the following frame bindings currently present:
{?y = 4}
And are then asked to "extendIfPossible" a binding from ?z to ?y.
With that "*" check present, when asked to extend "?z" with "?y", we see that "?y" is already bound to 4, and then recursively try to unify "?z" with "4", which results with us extending the frame with "?z = 4".
Without the check, we would end up extending the frame with just "?z = ?y". But in both cases, so long as ?z is not already bound to something else, I don't see the problem.
Note, if ?z had already been bound to something else then the code doesn't reach the part marked "*" (we would have already recursed to unifying with what ?z had already been matched to).
After thinking it over, I have realised that there might be some sort of argument for generating a "simplest" MGU (Most General Unifier). e.g. you might want an MGU with a minimal number of variables referring to other variables... that is, we'd rather generate the substitution {?x = 4, ?y = 4} than the substitution {?x = ?y, ?y = 4}... but I don't think this algorithm would guarantee this behaviour in any case, because if you asked it to unify "(?x 4)" with "(?y ?y)" then you would still end up with {?x = ?y, ?y = 4}. And if the behaviour can't be guaranteed, why the additional complexity?
Is my reasoning here all correct? If not, what's a counter example where the "*" check is necessary to generate a correct MGU?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这是个好问题!
我认为原因是您不想以诸如
{ ?x = ?y, ?y = ?x }
之类的循环绑定结束。 特别是,如果您省略了检查,则将(?x ?y)
与(?y ?x)
统一将为您提供上面的圆形框架。 通过检查,您将得到预期的帧 { ?x = ?y } 。框架中的循环绑定是不好的,因为它们可能会导致使用框架执行替换的函数(例如实例化)在无限循环中运行。
That's a good question!
I think the reason is that you don't want to end up with circular bindings such as
{ ?x = ?y, ?y = ?x }
. In particular, unifying(?x ?y)
with(?y ?x)
would give you the circular frame above if you omitted the check. With the check, you get the frame { ?x = ?y } as expected.Circular bindings in a frame are bad, because they may cause functions performing substitutions using the frame, such as
instantiate
, to run in an infinite loop.没有它,您将无法获得最通用统一器。 仍有工作要做:统一 x 和 y。
Without it, you wouldn't get the most general unifier. There'd still be work to be done: unifying x and y.