Казалось бы, ненужный случай в алгоритме унификации в SICP

Я пытаюсь понять алгоритм объединения, описанный в SICP здесь

В частности, в процедуре "extend-if-возможный" есть проверка (первое место, помеченное звездочкой "*"), которая проверяет, является ли правое выражение "выражение" переменной, которая уже связана с чем-то в текущий кадр:

(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}

И затем просят "расширить IfPossible" привязки от? 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}. И если поведение не может быть гарантировано, почему дополнительная сложность?

Все мои рассуждения верны? Если нет, то каков встречный пример, где проверка "*" необходима для генерации правильного MGU?

2 ответа

Решение

Это хороший вопрос!

Я думаю, причина в том, что вы не хотите заканчивать круговыми привязками, такими как { ?x = ?y, ?y = ?x }, В частности, объединяющий (?x ?y) с (?y ?x) даст вам круговую рамку выше, если вы опустите чек. С проверкой вы получите кадр {? X =? Y}, как и ожидалось.

Круговые привязки в кадре плохие, потому что они могут вызывать функции, выполняющие замены с использованием кадра, такие как instantiate, чтобы запустить в бесконечном цикле.

Без этого вы бы не получили самый общий объединитель. Будет еще много работы: объединение х и у.

Другие вопросы по тегам