Как сохранить функцию в переменной в Лиспе и использовать ее
Я хочу сохранить функцию как print
в переменной, так что я могу просто напечатать что-то короткое, как p
Например:
В Scheme
:
(define print display)
(print "Hello world\n")
;; alternate way
(define print 'display)
((eval print) "Hello world\n")
Тот же подход, похоже, не работает в Common Lisp
:
(defvar p 'print)
;;(print (type-of p))
(p "Hello world") ;; Attempt 1
((eval p) "Hello world") ;; >> Attempt 2
((eval (environment) p) "Hello world") ;; Attempt 3
Я получаю эту ошибку с Attempt 1
выше:
*** - EVAL: undefined function P
И это с Attempt 2
а также 3
в Clisp
:
*** - EVAL: (EVAL (ENVIRONMENT) P) is not a function name; try using a
symbol instead
*** - EVAL: (EVAL P) is not a function name; try using a symbol instead
И с gcl
:
Error: (EVAL P) is invalid as a function.
Error: (EVAL (ENVIRONMENT) P) is invalid as a function.
Так:
- Что значит
try using a symbol
имею в виду?p
определенноsymbol
; ложно положительный? - Что случилось с
eval
? Разве оценкаp
сдать процедуруprint
? - я думал
Lisp
процедуры былиfirst class objects
, ПочемуAttempt 1
не работает, как вScheme
?
РЕДАКТИРОВАТЬ
(Перенесено из комментария ниже)
Мне было интересно почему (setf (symbol-function 'p) #'print)
не будет работать таким образом(setf (symbol-function 'p) 'print)
, Я получаю следующую (не очень полезную) ошибку:
*** - SYSTEM::%PUTD: PRINT is not a function ;; CLisp
Error: PRINT is not of type LIST. ;; Gcl
Я знаю, что острый знак (#
) должен различать функцию и переменную
с тем же именем, но в этом случае есть только один print
, функция.
Кроме того, почему это не будет работать с defvar
вместо setf
вот так:
(defvar (symbol-function 'p) #'print)
еще defvar
а также setf
оба присваивают значения переменной.
Связанная ошибка:
*** - DEFVAR: non-symbol (SYMBOL-FUNCTION 'P) cannot be a variable ;; Clisp
Error: (SYMBOL-FUNCTION (QUOTE P)) is not of type SYMBOL. ;; Gcl
3 ответа
Common Lisp - это "Лисп-2". Помимо прочего, первая позиция в вызове функции оценивается в "пространстве имен функции". В вашем случае символ p
называет переменную, а не функцию.
Это работает лучше:
(defvar p 'print)
(funcall p "Hello world")
Или, возможно, но вы, вероятно, не хотите делать это:
(setf (symbol-function 'p) #'print)
(p "Hello world")
Common Lisp имеет отдельное пространство имен для функций, что делает подобные операции более многословными, чем со Scheme. Если вы хотели бы похож на верхний уровень (define p display)
в CL вы должны сделать макрос:
(defmacro defun-alias (name original-name)
`(setf (symbol-function ',name) #',original-name))
Который работает так:
(defun-alias pc princ)
(pc "Hello") ; prints hello
В отличие от схемы define
это только перезапишет глобальную привязку. Таким образом:
(flet ((test (x) (+ x x)))
(defun-alias test +)
(test 10))
установит глобальное определение #'test
в #'+
и вернуть 20. например. это работает как defun
,
Чтобы дополнить другие хорошие ответы прямыми ответами на ваши вопросы:
Что означает попытка использования символа? р определенно символ; ложно положительный?
Прочитайте сообщения об ошибках буквально: (EVAL (ENVIRONMENT) P)
а также (EVAL P)
(без оценки) не символы, а списки. В Common Lisp автомобиль формы не оценивается.
Что случилось с Eval? Разве оценка p не приводит к печати процедуры?
eval
никогда не вызывается вашим кодом (см. предыдущий ответ). Даже если бы это было, результатом было бы symbol-value
символа print
, не symbol-function
/fdefinition
,
Я думал, что процедуры Лисп были первоклассными объектами. Почему Попытка 1 не работает как в Схеме?
Это не имеет ничего общего с функциями (я думаю, что стандарт Common Lisp не использует термин "процедуры", как это делают стандарты Scheme). Это объекты первого класса. Это работает в Common Lisp:
(let ((p #'print))
(funcall p "hello world"))
Редактировать:
Ответы на дополнительные вопросы:
Мне было интересно почему
(setf (symbol-function 'p) #'print)
не будет работать таким образом(setf (symbol-function 'p) 'print)
,
Это не совсем так, что "резкий знак (#) должен различать функцию и переменную с тем же именем", как вы напишете позже. 'print
расширяется до (quote print)
так что он оценивает символ print
вместо его значения в качестве переменной. #'print
расширяется до (function print)
, поэтому он оценивает значение функциональной ячейки символа print
вместо. Будь то print
в настоящее время имеет значение как переменная, совершенно не имеет отношения к тому, что #'print
оценивает до.
настройка (symbol-function 'p)
к символу print
очевидно не сделает p
вызвать функцию print
потому что символ print
не совпадает с функцией, связанной с символом print
,
Кроме того, почему он не будет работать с defvar вместо setf следующим образом:
(defvar (symbol-function 'p) #'print)
но defvar и setf оба присваивают значения переменной.
setf
присваивает значения местам. Семестр (symbol-function 'p)
обозначает место, которое является функциональной ячейкой символа p
,
defvar
определяет новые глобальные переменные. Его первый аргумент должен быть символом, который называет переменную и не может быть каким-либо местом.