Равенство функциональных указателей - PullRequest
2 голосов
/ 19 июня 2019

Всегда ли два объекта функции Common Lisp с одинаковым обозначением символа eq? Например, это сравнение, кажется, работает:

(defun foo (fn)
  (let ((ht (make-hash-table)))
    (eq (symbol-function (hash-table-test ht)) fn)))

FOO
* (foo #'eql)
T
*(foo #'equal)
NIL

Но это может зависеть от реализаций, не создающих скрытых копий функций, предположительно из соображений эффективности. Поскольку hash-table-test возвращает символьный указатель, другой (возможно, лучшей) альтернативой eq будет вывод символа из функционального объекта? Один подход лучше другого?

Ответы [ 2 ]

4 голосов
/ 19 июня 2019

Всегда ли два объекта функции Common Lisp с одним и тем же обозначением символа всегда равны?

В Common Lisp функция - это кусок кода, независимо от того, скомпилирован он или нет. Из Глоссарий :

function n. 1. объект , представляющий код, который может быть вызван с нулевым или большим количеством аргументов и который генерирует ноль или более значений. 2. объект типа функции.

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

обозначение функции n. обозначение функции; то есть объект, который обозначает функцию и является одним из: символа (обозначающего функцию, названную этим символом в глобальной среде) или функции (обозначающей себя).

Таким образом, символ, который является обозначением функции, является чем-то таким, что при оценке в определенном контексте или с определенным синтаксисом, таким как #'symbol или (function symbol), производит функцию, а сравнение двух указателей функции является сравнение функций, которые они обозначают:

CL-USER> (eql #'car #'cdr)
NIL

CL-USER> (eql #'car (symbol-function 'car))
T

Но обратите внимание, что этот тест на равенство представляет собой просто сравнение идентичности функциональных объектов (фрагментов кода), как в:

CL-USER> (eq #'car #'car)
T
CL-USER> (let ((a (lambda (x) (1+ x))))
          (eq a a))
T

но не фактических байтов, которые их представляют (код!):

CL-USER> (let ((a (lambda (x) (car x)))
          (eq a #'car))
NIL
CL-USER> (defun f (x) (1+ x))
F
CL-USER> (defun g (x) (1+ x))
G
CL-USER> (equalp (function f) (function g))
NIL
CL-USER> (equalp (lambda (x) (1+ x)) (lambda (x) (1+ x)))
NIL

Обратите внимание, что во всех этих случаях две сравниваемые функции имеют не только одинаковое «значение», но в большинстве случаев одинаковый «исходный код», компилируются одинаково и ведут себя одинаково на одних и тех же входных данных. , Это связано с тем, что математически функция - это (возможно) бесконечный набор пар (вход, выход), и нельзя сравнивать зараженные объекты.

Но это может зависеть от реализаций, не создающих скрытых копий функций, предположительно из соображений эффективности.

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

Поскольку hash-table-test возвращает символьное обозначение, другой (возможно, лучшей) альтернативой eq будет вывод символа из объекта функции? Один подход лучше другого?

(я полагаю, вы хотите назначить обозначение функции вместо обозначения символа)

На самом деле, hash-table-test обычно возвращает обозначение функции только в виде символа, как сказано в руководстве :

test --- обозначение функции. Для четырех стандартизированных тестовых функций хеш-таблицы (см. Make-hash-table) возвращаемое тестовое значение всегда является символом. Если реализация разрешает дополнительные тесты, зависит от того, возвращаются ли такие тесты как объекты функций или имена функций.

Итак:

CL-USER> (type-of (hash-table-test (make-hash-table)))
SYMBOL
CL-USER> (eq 'eql (hash-table-test (make-hash-table)))
T
CL-USER> (eq #'eql (hash-table-test (make-hash-table)))
NIL

Обратите внимание, что в последнем случае мы сравниваем функцию (значение #'eql) с символом (что возвращает hash-table-test), и, очевидно, это сравнение возвращает ложное значение.

В заключение:

  1. Не очень разумно сравнивать функции, если только вы не хотите знать, действительно ли две функции являются одним и тем же объектом в памяти (например, если две вещи - это один и тот же скомпилированный код).

  2. Всегда важно отличать функции от их обозначений в виде символов (имен функций) или списков, таких как (LAMBDA parameters body), и решать, что мы хотим на самом деле сравнивать.

3 голосов
/ 19 июня 2019

#'eql эквивалентно (function eql).Если нет привязки лексической функции eql, это определено для возврата определения глобальной функции символа eql.Это также то, что (symbol-function 'eql) определено для возврата.

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

(eq #'f (symbol-function 'f))

всегда должно быть истинным.

...