По-видимому, ненужный случай в алгоритме унификации в 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)))))
В соответствующем комментарии говорится:
"В первом случае, если переменная, которую мы пытаемся сопоставить, не привязана, но значение, которое мы пытаемся сопоставить с ней, само является переменной (другой), необходимо проверить, и если да, чтобы соответствовать его значению. Если обе стороны в матче несвязаны, мы можем привязать их к другому."
Однако я не могу придумать случай, когда это действительно необходимо.
О чем он говорит, я думаю, это то, где вы можете иметь следующие привязки фреймов в настоящее время:
{?y = 4}
И затем попросят "extendIfPossible" привязку от? z до? y.
С этой проверкой "*", когда ее просят расширить "? z" на "? y", мы видим, что "? y" уже привязано к 4, а затем рекурсивно пытается объединить "? z" с "4", что приводит к тому, что мы расширяем рамку с помощью "? Z = 4".
Без проверки мы закончили бы расширение рамки только "? z =? y". Но в обоих случаях, если? Z уже не привязан к чему-то другому, я не вижу проблемы.
Обратите внимание, что если? z уже привязано к чему-то другому, тогда код не доходит до части с пометкой "*" (мы бы уже перечислили, чтобы объединиться с тем, что? z уже было сопоставлено).
Подумав об этом, я понял, что может быть какой-то аргумент для создания "простейшего" MGU (Most General Unifier). например вам может понадобиться MGU с минимальным числом переменных, относящихся к другим переменным... то есть мы скорее сгенерируем подстановку {? x = 4,? y = 4}, чем замещение {? x =? y,? y = 4}... но я не думаю, что этот алгоритм в любом случае гарантирует это поведение, потому что если вы попросите его объединить "(? x 4)" с "(? y? y)", то вы все равно в конечном итоге с {? x =? y,? y = 4}. И если поведение не может быть гарантировано, зачем нужна дополнительная сложность?
Я правильно рассуждаю здесь? Если нет, то какой встречный пример, где проверка "*" необходима для создания правильного МГУ?
Ответы
Ответ 1
Это хороший вопрос!
Я думаю, причина в том, что вы не хотите заканчивать круговыми связями, такими как { ?x = ?y, ?y = ?x }
. В частности, объединение (?x ?y)
с (?y ?x)
даст вам круговой фрейм выше, если вы опустили чек. С проверкой вы получите кадр {? X =? Y}, как ожидалось.
Циклические привязки в кадре плохие, потому что они могут вызывать функции, выполняющие подстановки с использованием фрейма, например instantiate
, для запуска в бесконечном цикле.
Ответ 2
Без этого вы не получите унифицированный общий. Все еще предстоит сделать работу: объединение x и y.