Определение собственного макроса для проверки условия - Lisp
Формулировка задачи:
(OUR-IF test :THEN exp1 exp2… :ELSE exp3 exp4 …)
> (our-if (> 3 1) :then ‘ok) OK > (our-if (< 5 3) :else ‘ok) OK > (our-if (> 3 1) :else ‘oops) NIL > (our-if (> 3 1) :then) NIL > (our-if (> 3 1) :else ‘oops :then ‘ok) ok > (our-if (> 3 1) :then ‘ok :else ‘oops) ok
(defmacro our-if (test &rest body)
(let* ((bt (position :then body))
(et (position :else body))
(th (cdr (member :then body)))
(el (cdr (member :else body))))
`(cond
(,test, nil ,@th)
(t ,@el))))Решение задачи: «Определение собственного макроса для проверки условия»
(defmacro our-if (&body body) (destructuring-bind (test-form &optional body-clause) (split-sequence :then body) (when body-clause (destructuring-bind (then-form &optional else-form) (split-sequence :else body-clause) `(if (progn ,@test-form) (progn ,@then-form) (progn ,@else-form)))))) (macroexpand '(our-if 1)) ;; NIL ;; T (macroexpand '(our-if 1 :then (foo) (bar))) ;; (IF (PROGN 1) ;; (PROGN (FOO) (BAR)) ;; (PROGN)) ;; T (macroexpand '(our-if 1 :then (foo) (bar) :else (baz))) ;; (IF (PROGN 1) ;; (PROGN (FOO) (BAR)) ;; (PROGN (BAZ))) ;; T
Объяснение кода листинга программы
В этом коде определён макрос с именем our-if. Этот макрос принимает произвольное количество аргументов, которые могут быть либо одним выражением, либо последовательностью выражений, заключённых в begin и разделённых запятыми. Аргументы, которые являются последовательностью выражений, разделённых запятыми, заключены в split-sequence. Это выражение либо помещается в then-form, либо в else-form в зависимости от того, есть ли у аргумента заголовок :then.
В этом макросе используется вспомогательный макрос split-sequence, который принимает два аргумента: последовательность для разбиения и заголовок, который определяет, где поместить результат разбиения. Если заголовок :then, то результат помещается в then-form, если :else, то в else-form.
Синтаксис этого макроса напоминает синтаксис встроенного макроса if, за исключением того, что вместо then-form и else-form используется test-form, then-clause и else-clause.
Когда макрос our-if вызывается с аргументом :then и без :else, результат будет nil, потому что он ожидает два аргумента после :then.
Когда макрос our-if вызывается с аргументами :then и :else, и без аргументов, результат будет nil, потому что он ожидает по крайней мере один аргумент после каждого из ключевых слов :then и :else.
Когда макрос our-if вызывается с аргументами :then и :else, и с аргументами после каждого из ключевых слов :then и :else, результат будет nil, потому что он ожидает еще одного аргумента после каждого из ключевых слов :then и :else.
Когда макрос our-if вызывается с аргументами :then и :else, и с аргументами после каждого из ключевых слов :then и :else, и без аргументов, результат будет nil, потому что он ожидает по крайней мере одного аргумента после каждого из ключевых слов :then и :else.