Чтение лямбда-выражений с клавиатуры в Common Lisp - PullRequest
7 голосов
/ 13 июня 2019

Я хочу иметь возможность читать лямбда-выражение с клавиатуры. Например, если квадрат функции уже ОТКЛЮЧЕН, я могу ввести имя символа:

(defun square (x) (* x x))

так, чтобы при оценке оценивалось следующее:

(funcall (read) 2)

пользователь может ввести square, и в результате получится 4. Но если пользователь введет

(lambda (x) (* x x))

результатом является ошибка, например в Macintosh Common Lisp,

Ошибка: (LAMBDA (X) (* X X)) не может быть вызвано или применено

Есть ли простой способ сделать это, которого мне не хватает?

Спасибо.

Ответы [ 4 ]

9 голосов
/ 13 июня 2019

read возвращает list, которое должно быть оценено , прежде чем оно может быть funcall ed.

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

(funcall (read) 2)
#.(lambda (x) (* x x))
==> 4

Однако, вообще говоря, это дыра в безопасности (вы оцениваете предоставленный пользователем код - чтоесли они набрали #.(start-nuclear-war)?), поэтому осторожный инженер свяжет *read-eval* с nil при чтении ввода, который они не контролируют.

Таким образом, гораздо лучше использовать coerce явно:

(funcall (coerce (let ((*read-eval* nil)) (read)) 'function) 2)
1+
==> 3
(funcall (coerce (let ((*read-eval* nil)) (read)) 'function) 2)
(lambda (x) (* x x))
==> 4
7 голосов
/ 13 июня 2019

Поскольку вы используете read, в общем случае вам нужно будет оценить возвращаемые формы, чтобы получить значимые значения.Но в вашем конкретном случае вы можете использовать COERCE.Например, из REPL:

CL-USER> (coerce '+ 'function)
#<FUNCTION +>

Вышеприведенное находит функцию, для которой символ + равен fbound .

CL-USER> (coerce '(lambda (x) (* x x)) 'function)
#<FUNCTION (LAMBDA (X)) {53F2BF2B}>

Вышеприведенное берет лямбда-выражение и превращает его в функциональный объект.

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

Вы получаете эту ошибку, потому что READ возвращает только список (LAMBDA (X) (* x x)), он не оценивает его как функцию. Для этого вам нужно написать:

(funcall (eval (read)) 2)

Обратите внимание, что в этом случае просто запись square больше не работает, пользователю теперь нужно будет ввести #'square.

2 голосов
/ 13 июня 2019
CL-USER 8 > (defun read-function (&optional (stream *standard-input*))
              (let ((f (read stream)))
                (cond (; function object
                       (functionp f) f)
                      (; symbol naming a function
                       (symbolp f) (symbol-function f))
                      (; (function f)
                       (and (consp f)
                            (eq (first f) 'function))
                       (eval f))
                      (; (lambda ...)
                       (and (consp f)
                            (eq (first f) 'lambda))
                       (eval f)))))
READ-FUNCTION

Примеры:

CL-USER 9 > (read-function)
#.#'+
#<Function + 40F0044AD4>

CL-USER 10 > (read-function)
+
#<Function + 40F0044AD4>

CL-USER 11 > (read-function)
#'+
#<Function + 40F0044AD4>

CL-USER 12 > (read-function)
(lambda (a b) (+ a b))
#<anonymous interpreted function 4060000C8C>
...