Как может eval обрабатывать функцию таким образом

Я пытаюсь понять, как работает princ данных с заменой кода при использовании чего-то вроде eval в функции, которая позволяет мне передавать любые команды печати, такие как print princ т.д., И использовать эту команду в своем представлении:

(defun print-test (fn text)
 (eval '(fn 'text)))

Я пробовал разные способы, но я не могу заставить это работать. Я также пробовал:

(defun print-test (fn text)
 (eval ('fn 'text)))

.. и другие варианты. Поэтому я явно что-то пропускаю. Я хотел бы иметь возможность сделать это:

(print-test 'princ 'some-text)

Я обычно получаю ошибку fn is undefined. Но поскольку я оцениваю код в реальном времени, я думаю, он может получить fn с моего ввода?

Я понимаю, что есть другие способы сделать это, например, передать фактический объект функции, например #'princ но мне любопытно, как механизм eval работает для генерации кода "на лету".

1 ответ

Ошибка, такая как fn is undefined не princ не говорит вам, что princ не определен, но символ fn не имеет привязки к функции. Вы не хотите вызывать символ fn, вы хотите вызвать значение переменной fn.

Похоже, вы хотите оценить список, первым элементом которого является значение fn а вторым элементом является значение text. Вы бы создать такой список, используя функцию list в (list fn text). Тогда вы можете вызвать eval с ним:

(defun print-test (fn text)
 (eval (list fn text)))

Вы также можете использовать нотацию backquote и делать

(defun print-test (fn text)
 (eval '(,fn ,text)))

В этих случаях, если вы хотите иметь тот же эффект, что и (princ 'hi), вам нужно, чтобы fn был символом princ а text - списком 'hi (да, список, так как 'hi является сокращением для (quote hi)). Вы бы назвали это так:

(print-test 'princ ''hi)

Если второй аргумент должен всегда указываться в сгенерированном тексте, вы могли бы также выполнить любое из следующих действий:

(eval '(,fn ',text))
(eval '(,fn (quote ,text)))
(eval (list fn (list 'quote text)))

Однако все это говорит о том, что это очень странный способ использовать eval. Если у него нет особых оснований для этого, почему бы просто не использовать funcall? В конце концов, если fn является законным как автомобиль формы для оценки, а text является аргументом, не может ли это быть просто следующим?

(defun print-test (fn text)
 (funcall fn text))

Разумеется, это будет немного иначе. Здесь, если вы хотите получить тот же эффект, что и (princ 'hi), вы просто передадите символы princ и hi:

(print-test 'princ 'hi)

licensed under cc by-sa 3.0 with attribution.