Ракетка - Макро - Определить функцию

Итак, я хочу создать макрос, который облегчит написание функций, которые принимают s-выражение и список в качестве входных данных и что-то делают, когда одно из s-выражений в списке совпадает с s-выражением во входных данных., Я заметил, что многие из этих функций имеют одинаковый внешний вид.

Итак, я написал это:

(define-syntax (ember stx)
  (syntax-parse stx
    [(ember name terminal match recurse) #'(define name (lambda (s l)
                                                          (cond
                                                            [(null? l) terminal]
                                                            [(eq? (car l) s) match]
                                                            [else recurse])))]))

(ember member #f #t (match s (cdr l)))

К сожалению, это не работает, потому что s является несвязанным идентификатором. Я попытался заключить его в кавычку, а затем поместить в макрос eval, но это также привело к появлению несвязанного идентификатора.

Итак, я переписал свой код следующим образом, и он работает:

(define-syntax (ember stx)
  (syntax-parse stx
    [(ember name terminal match recurse) #'(define name (lambda (s l)
                                                          (cond
                                                            [(null? l) (terminal s l)]
                                                            [(eq? (car l) s) (match s l)]
                                                            [else (recurse s l)])))]))

(ember member (lambda (x y) #f) (lambda (x y) #t) (lambda (x y) (match x (cdr y))))

Но, к сожалению, если мое определение соответствия становится таким сложным, кажется, проще просто скопировать функцию-член и создать новые функции того же рода, отредактировав места, где параметры терминала, соответствия и рекурсии идут вручную.

Есть лучший способ сделать это?

2 ответа

Вы должны использовать параметры синтаксиса вместо datum-> синтаксис:

(define-syntax-parameter s (syntax-rules ()))
(define-syntax-parameter l (syntax-rules ()))

(define-syntax-rule
  (ember name terminal match recurse)
  (define name
    (lambda (s* l*)
      (syntax-parameterize ([s (make-rename-transformer #'s*)]
                            [l (make-rename-transformer #'l*)])
        (cond
          [(null? l) terminal]
          [(eq? (car l) s) match]
          [else recurse])))))

(ember member #f l (member s (cdr l)))
(member 'a '(1 2 a 3))
; -> '(a 3)

Посмотрите Поддержание этого Чистым с Синтаксическими Параметрами для деталей относительно того, почему.

Вы не можете взять s а также l из воздуха, но убедитесь, что они совпадают с вашими данными. Это делает это:

(define-syntax (ember stx)
  (syntax-parse stx
    [(ember name terminal match recurse)
     (with-syntax ([s (datum->syntax stx 's)]
                   [l (datum->syntax stx 'l)])
       #'(define name (lambda (s l)
                        (cond
                          [(null? l) terminal]
                          [(eq? (car l) s) match]
                          [else recurse]))))]))

(ember member2 #f l (member2 s (cdr l)))
(member2 'a '(1 2 a 3))
; ==> (a 3)
Другие вопросы по тегам