Вот решение syntax-rules
, основанное на отзывах, которые я получил в другом ответе и комментариях:
(define ($ alist name)
(cdr (assoc name alist)))
(define-syntax with-alist
(syntax-rules ()
[(_ alist names expr)
(let ([alist-local alist])
(apply map (lambda names expr)
(map (lambda (name) ($ alist-local name)) (quote names))))]))
Вот пример использования:
> (define alist-example
'((x 1 2 3) (y 4 5 6) (z 7 8 9)))
> (with-alist alist-example (x) (+ x 2))
(3 4 5)
> (with-alist alist-example (x y) (+ x y))
(5 7 9)
> (with-alist alist-example (x y z) (+ x y z))
(12 15 18)
Этот ответ останавливается на решении более сложного примера, (with-alist alist-example (/ y (apply max y)))
, в моем вопросе, но я думаю, что это разумный подход для моих целей:
> (with-alist alist-example (y) (/ y (apply max ($ alist-example 'y))))
(2/3 5/6 1)
РЕДАКТИРОВАТЬ: После некоторых дополнительных действий я пришел к немного другое решение, которое, я думаю, обеспечит большую гибкость.
Мой новый макрос npl
расширяет сокращенные выражения в список имен и процедур.
(define-syntax npl
(syntax-rules ()
[(_ (names expr) ...)
(list
(list (quote names) ...)
(list (lambda names expr) ...))]))
Вывод этого макроса передается обычной процедуре with-list-map
, которая содержит большинство основных функций в макросе with-alist
выше.
(define (with-alist-map alist names-proc-list)
(let ([names-list (car names-proc-list)]
[proc-list (cadr names-proc-list)])
(map (lambda (names proc)
(apply map proc
(map (lambda (name) ($ alist name)) names)))
names-list proc-list)))
3 приведенных выше примера использования with-alist
могут быть записаны в одном вызове with-alist-map
.
> (with-alist-map alist-example
(npl ((x) (+ x 2))
((x y) (+ x y))
((x y z) (+ x y z))))
((3 4 5) (5 7 9) (12 15 18))