Как проверить, совпадают ли две функции?

Я нашел фрагмент кода где-то на сайте:

(letrec
  ([id (lambda (v) v)]
   [ctx0 (lambda (v) `(k ,v))]
         .....
         .....
         (if (memq ctx (list ctx0 id)) <---- condition always return false
         .....

где ctx также является функцией:

однако я никогда не мог заставить тест-утверждение вернуть true.

тогда у меня есть следующий тест:

(define ctx0 (lambda (v) `(k ,v)))
(define ctx1 (lambda (v) `(k ,v)))
(eq? ctx0 ctx1)
=> #f
(eqv? ctx0 ctx1)
=> #f
(equal? ctx0 ctx1)
=> #f

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

но если функции можно сравнить с другими функциями, как я могу проверить, являются ли две функции же? а что, если у них разные имена переменных? например:

(lambda (x) (+ x 1)) и (lambda (y) (+ y 1))

P. S. Я использую DrRacket для проверки кода.

2 ответов


ты не можешь. функции рассматриваются как непрозрачные значения: они сравниваются только по идентичности, не более того. это дизайн.


но почему? не могли бы языки реализовать значимые способы сравнения функций, которые иногда могут быть полезны? Ну, не совсем, но иногда трудно понять почему без подробностей. Рассмотрим ваш пример из вашего вопроса-эти две функции кажется эквивалент:

(define ctx0 (lambda (v) `(k ,v)))
(define ctx1 (lambda (v) `(k ,v)))

и действительно, они. Но что даст сравнение этих функций для равенства? В конце концов, мы могли бы так же легко реализовать другую функцию:

(define ctx2 (lambda (w) `(k ,w)))

эта функция, для всех намерений и целей, одинаковых к предыдущим двум, но это провалит наивную проверку равенства!

чтобы решить, эквивалентны ли два значения, мы должны определить некоторый алгоритм, который определяет равенство. С учетом примеры, которые я привел до сих пор, такой алгоритм кажется очевидным: две функции должны считаться равными, если (и только если) они α-эквивалент. С этим в руке мы можем теперь осмысленно проверить, равны ли две функции!

...правильно?

(define ctx3 (lambda (v) (list 'k v)))

Ох, нет. Эта функция делает то же самое, но она не реализована точно так же, поэтому она не выполняет нашу проверку равенства. Конечно, мы можем это исправить. Квазиквотация и используя list конструктор в значительной степени одинаковы, поэтому мы можем определить их как эквивалентные в большинстве случаев.

(define ctx4 (lambda (v) (reverse (list v 'k))))

Гах! Это также функционально эквивалентно, но все равно не соответствует нашему эквивалентному алгоритму. Как мы можем это сделать?


оказывается, мы не можем, правда. Функции являются единицами абстракции-по своей природе мы не предполагается, что нужно знать, как они реализованы, только то, что они делают. Это означает, что равенство функций действительно может быть правильно определено только в терминах оперативные эквивалентности; то есть реализация не имеет значения, имеет значение только поведение.

проблема останова.

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

равенство функций очень соблазн проблема хотеть решайте, когда простые случаи кажутся такими легкими, но большинство языков используют правильный подход и даже не пытаются. Это не значит, что это не полезная идея: если бы это было возможно, это было бы невероятно полезно! Но поскольку это не так, вам придется использовать другой инструмент для работы.


семантически, две функции f и g равны, если они согласен для каждого входа, т. е. если для всех x, мы (= (f x) (g x)). Конечно, нет никакого способа проверить это для каждый возможное значение x.

если все вы хотите сделать, это быть достаточно уверен, что (lambda (x) (+ x 1)) и (lambda (y) (+ y 1)) то же самое, тогда вы можете попробовать утверждать, что

(map (lambda (x) (+ x 1)) [(-5) (-4) (-3) (-2) (-1) 0 1 2 3 4 5])

и

(map (lambda (y) (+ y 1)) [(-5) (-4) (-3) (-2) (-1) 0 1 2 3 4 5])

то же самое в вашем модульное тестирование.