【问题标题】:Mapping within macro without extra parentheses?在没有额外括号的宏内映射?
【发布时间】:2017-07-27 08:20:32
【问题描述】:

假设我有一个这样的宏:

(define-syntax (choose stx)
  (define data (syntax->datum stx))
  (define args (cadr data))
  (define body (cddr data))
  (define output
    `(apply (case (car ,args)
              ,(map (lambda (choice)
                           `((,(car choice)) ,(cadr choice)))
                         body)
              (else (displayln "error")))
            (cdr ,args)))
  (println output)
  #'(void))

如果我在这样的事情上使用它(可能有更多选择):

(choose args
        ("run" runsomething)
        ("del" delsomethingelse))

将其转换为

(apply
 (case (car args)
   ((("run") runsomething)
    (("del") delsomethingelse))
   (else (displayln "error")))
 (cdr args))

这不是有效的代码,因为地图给了它额外的括号。相反,我希望它给我这个:

(apply
 (case (car args)
   (("run") runsomething)
   (("del") delsomethingelse)
   (else (displayln "error")))
 (cdr args))

我怎么能做这样的事情?

【问题讨论】:

    标签: macros scheme racket


    【解决方案1】:

    使用unquote-splicing(又名,@)删除map周围的列表。

    例子:

    (define xs '(a b c))
    `(1 2 ,xs  3 4)    ; => '(1 2 (a b c) 3 4)
    `(1 2 ,@xs 3 4)    ; => '(1 2 a b c 3 4)
    

    但是我注意到您在输入 stx 上使用了 syntax->datum 的语法转换器。这会删除词汇信息, 最终可能会导致问题。它建议使用syntax-casesyntax-parse,它使用模式匹配来挑选元素 输入语法和模板以生成输出。

    (define-syntax (choose stx)
      (syntax-case stx ()
        [(_choose args
                  (datum fun-expr)
                  ...)
         #'(apply (case (car args)
                    [(datum) fun-expr]
                    ...)
                  (cdr args))]))
    
    (define (run-it . xs) (list 'ran-it  xs))
    (define (del-it . xs) (list 'delt-it xs))
    
    (choose (list "run" 1 2 3)
            ("run" run-it)
            ("del" del-it))
    
    Output: '(ran-it (1 2 3))
    

    【讨论】:

    • 你说syntax->datum去掉了词法信息,但是datum->syntax的第一个参数(ctxt)不就解决了吗?
    • 没有。 datum->syntax 方法为所有内容提供了相同的上下文(即选择形式)。 args 和 fun-exprs 可能有不同的上下文。
    • 示例:如果 args 由一个宏生成,而 fun-exprs 由另一个宏生成,则上下文不同。
    • 所以它通常可以正常工作,但不能与其他宏结合使用?
    • 如果(choose ...) 中的所有内容都是“手工”编写的,那么它应该可以使用您的datum->syntax 方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-23
    • 2019-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多