Таким образом, ваш код может быть написан так в стандартном Common Lisp:
(let ((it x-expression))
(if it
(let ((it (f it)))
(if it
it))))
Обратите внимание, что я не даю форму else (альтернативу), поскольку она необязательна в CL.
Пол Грэм представляет анафорические макросы для автоматического кэширования значения теста.
(defmacro aif (test-form then-form &optional else-form)
`(let ((it ,test-form))
(if it ,then-form ,else-form)))
(aif x-expression
(aif (f it)
(aif (f it)
it)
Это хорошо работает в CL, но создает проблемы с гигиеной в Scheme. Я знаю, что кто-то создал что-то с дополнительным параметром для имени привязки, но это теряет элегантность:
(aif x x-expression
(aif y (f x)
(aif z (f y)
z)))
Я экспериментировал с cond
При создании оценщиков я обычно тестирую прежде, чем смогу очистить, и это заканчивается вложением if
и let
. Моя первая итерация выглядела так:
(define (ev expr env)
(defcond
((symbol? expr) (symbol->value expr env))
((not (pair? expr)) expr => (operator (ev (car expr) env)))
((macro? operator) (macro-apply expr (cdr expr) env) => (args (map (lambda (e) (ev e env)) (cdr expr))))
(else (fun-apply operator args env))))
Он также поддерживал альтернативный способ, поскольку я нашел его не слишком элегантным для повторного использования =>:
(define (ev expr env)
(defcond
((symbol? expr) (symbol->value expr env))
((not (pair? expr)) expr)
(define operator (ev (car expr) env))
((macro? operator) (macro-apply expr (cdr expr) env))
(define args (map (lambda (e) (ev e env)) (cdr expr)))
(else (fun-apply operator args env))))
Теперь это можно использовать так же, как и aif
. Если вас интересует макрос Scheme:
(define-syntax defcond
(syntax-rules (else bind define =>)
((_ "build" terms ())
terms)
((_ "build" alternative ((bind (b e) ...) . rest))
(defcond "build" (let ((b e) ...) alternative) rest))
((_ "build" alternative ((bind name (b e) ...) . rest))
(defcond "build" (let name ((b e) ...) alternative) rest))
((_ "build" alternative ((define b e) . rest))
(defcond "build" (letrec ((b e)) alternative) rest))
((_ "build" alternative ((predicate consequent) . rest))
(defcond "build" (if predicate consequent alternative) rest))
((_ "build" alternative ((predicate consequent => (b e) ...) . rest))
(defcond "build" (if predicate consequent (let ((b e) ...) alternative)) rest))
((_ "build" alternative ((predicate consequent) . rest))
(defcond "build" (if predicate consequent alternative) rest))
((_ "maybe-else" ((else expression) . rest))
(defcond "build" expression rest))
((_ "maybe-else" ((something expression) . rest))
(defcond "build" #f ((something expression) . rest)))
((_ "reverse" terms ())
(defcond "maybe-else" terms))
((_ "reverse" (fterms ...) (term1 terms ...))
(defcond "reverse" (term1 fterms ...) (terms ...)))
((_ terms ...)
(defcond "reverse" () (terms ...)))))
Не очень элегантная реализация, но она работает. Как видите, он поддерживает bind
и также называется bind
. например.
(defcond
((not (pair? lst)) #f)
(bind loop ((lst lst) (acc 0)))
((null? lst) acc)
(else (loop (cdr lst) (+ acc (car lst))))
Хотя мне нравится идея, я все еще не думаю, что она божественная и изящная, как это должно быть. Пока не появится лучший синтаксис, я буду писать его для удобства чтения. например:
(if (not (pair? lst))
#f
(let loop ((lst lst) (acc 0))
(if (null? lst)
acc
(loop (cdr lst) (+ acc (car lst))))))