Напишите функцию, которая ведет себя как car, cadr, caddr и т. Д. - PullRequest
1 голос
/ 09 апреля 2019

Я новичок в 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

1 Ответ

3 голосов
/ 09 апреля 2019

Такие функции, как 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))

Теперь, если выПри компиляции кода вы получите предупреждения во время компиляции о неизвестной функции, когда вы сможете заглушить предупреждения.

...