Напишите функцию, которая ведет себя как car, cadr, caddr и т. д

Я новичок в 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
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
229
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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))

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

замечательный! Таким образом, чтобы получить полный шаблон, вы используете механизм перезапуска обработчика! Большое спасибо за быстрый ответ! Я действительно начинаю понимать, почему Лисп такой мощный!!

Bandoos 09.04.2019 16:06

Отличный ответ — я почему-то никогда не слышал об обработчике undefined-function.

Ven 09.04.2019 17:18

Другие вопросы по теме