Я новичок в lisp (я экспериментирую с sbcl и ccl), я столкнулся с использованием car
и cdr
, которые можно произвольно связать в цепочку внутри одного вызова функции, например (caddr)
.
Я блуждал, как можно было бы написать функции, которые ведут себя так...
Скажем, например, я хотел бы my-eval
оценить ввод s-exp 3 раза, если я вызову его как (my-evaaal '(+ 2 1))
Я взломал свой путь с помощью макроса, например
(my-ev $$$$ '(...)) где поведение определяется количеством '$' в первом аргументе путем преобразования его в последовательность символов (coerce (symbol-name x) 'list)
и оценки и рекурсии до тех пор, пока список не станет равным нулю.. .
основная потребность:
;; if
(defvar *foo* 1)
(eval '*foo*) ;; => 1
(eval ''*foo*) ;; => *foo*
(eval '''*foo*) ;; => '*foo*
;; then
(eval (eval (eval '''*foo*))) ;; => 1
желаемый синтаксис
(my-eval '''*foo*) ;; => '*foo*
(my-evaal '''*foo*) ;; => *foo*
(my-evaaal '''foo) ;; => 1
Такие функции, как CAAR, CADR, являются обычными функциями; вы можете определить макрос, который поможет вам легко определить их, если хотите.
(defpackage :so (:use :cl :ppcre))
(in-package :so)
(defmacro eval%% (count form)
(case count
(0 form)
(1 `(eval ,form))
(t (check-type count (integer 2))
`(eval%% ,(1- count) (eval ,form)))))
Например, следующее:
(eval%% 3 '''most-positive-fixnum)
расширяется последовательно как:
(EVAL%% 2 (EVAL '''MOST-POSITIVE-FIXNUM))
(EVAL%% 1 (EVAL (EVAL '''MOST-POSITIVE-FIXNUM)))
(EVAL (EVAL (EVAL '''MOST-POSITIVE-FIXNUM)))
Затем вы можете определить пользовательские функции eval следующим образом или даже с помощью другого макроса:
(defun evaal (x) (eval%% 2 x))
(defun evaaal (x) (eval%% 3 x))
В качестве альтернативы обратите внимание, что вы можете перехватывать вызовы неопределенных функций:
(block nil
(handler-bind ((undefined-function
(lambda (e)
(return
(values (cell-error-name e)
(compute-restarts e))))))
(evaaaaaal 'a)))
=> EVAAAAAAL
(#<RESTART CONTINUE {7FD5F5F8CE43}> #<RESTART USE-VALUE {7FD5F5F8CE03}>
#<RESTART SB-KERNEL::RETURN-VALUE {7FD5F5F8CDC3}>
#<RESTART SB-KERNEL::RETURN-NOTHING {7FD5F5F8CD83}>
#<RESTART SWANK::RETRY {7FD5F5F8DA13}> #<RESTART ABORT {7FD5F5F8DEC3}>
#<RESTART ABORT {7FD5F5F8EB03}>)
Вы также можете использовать стандартный перезапуск USE-VALUE, чтобы предоставить другую функцию для вызова:
(defun multi-eval-handler (condition)
(let ((name (cell-error-name condition)))
(when (eq (symbol-package name) (find-package :so))
(register-groups-bind ((#'length count)) ("EV\(A+\)L" (string name))
(invoke-restart 'use-value (make-repeated-evaluator count))))))
Вам нужна вспомогательная функция, которая вычисляет оценку N раз:
(defun make-repeated-evaluator (count)
(case count
(0 #'identity)
(1 #'eval)
(t (check-type count (integer 2))
(lambda (form)
(loop
for value = form then (eval value)
repeat count
finally (return value))))))
Например:
(funcall (make-repeated-evaluator 3)
'''most-positive-fixnum)
=> 4611686018427387903
И тогда у вас могут быть сколь угодно длинные функции eval:
(handler-bind ((undefined-function #'multi-eval-handler))
(evaaaaaaaaaaaaaal '''''''''''''0))
Теперь, если вы скомпилируете код, у вас будут предупреждения во время компиляции о неизвестной функции, когда вы сможете приглушить предупреждения.
Отличный ответ — я почему-то никогда не слышал об обработчике undefined-function
.
замечательный! Таким образом, чтобы получить полный шаблон, вы используете механизм перезапуска обработчика! Большое спасибо за быстрый ответ! Я действительно начинаю понимать, почему Лисп такой мощный!!