【问题标题】:Why doesn't my Scheme macro work well?为什么我的 Scheme 宏不能正常工作?
【发布时间】:2016-11-11 03:32:40
【问题描述】:

我希望我的代码将结构转换为表达式,并对其求值以获得原始结构。这是我的代码:

(define-syntax expr-returning (syntax-rules ()
                           ((expr-returning (car x)) '(car (expr-returning x)))
                           ((expr-returning (cdr x)) '(cdr (expr-returning x)))
                           ((expr-returning (cons x y)) '(cons (expr-returning x) (expr-returning y)))
                           ((expr-returning obj) (cond [(null? obj)  'null]
                                                       [(number? obj)  'obj]
                                                       [(char? obj)  'obj]
                                                       [(boolean? obj) 'obj]
                                                       [(pair? obj) (expr-returning (cons (car obj) (cdr obj)))]
                                                       [else 'error]
                                                  ))))

这是我的测试:

(define ls (append '(a e i o u) 'y))
(define d (cons ls (cdr (cdr ls))))
(define e (expr-returning d))

d
e
(eval e)

输出是

'((a e i o u . y) i o u . y)
'(cons (expr-returning (car d1)) (expr-returning (cdr d1)))
cons: unbound identifier;
 also, no #%app syntax transformer is bound in: cons

我的缺点有什么问题?

【问题讨论】:

  • 我在 DrRacket 中尝试了您的示例,它有效:(eval e) => '((car (expr-returning d)) cdr (expr-returning d))

标签: scheme racket


【解决方案1】:

如果您使用#lang racket 作为您的语言选择来运行您的程序, 您需要使用正确的命名空间和eval

你可以使用这个:

(define ns (variable-reference->namespace (#%variable-reference)))
(eval e ns)

简而言之:您的宏正在运行。评估宏结果的示例不是(至少在#lang racket 中)。

还要注意,在“模块内部”和 REPL 中使用 eval 是有区别的。我敢打赌,Renzo 在 REPL 中测试了您的示例,它可以正常工作,并且您从模块内(即在定义窗口中)尝试了该示例。

【讨论】:

  • 是的,命名空间是必需的。
【解决方案2】:

查看您的代码并没有多大意义,因为宏扩展发生在代码运行之前。

(define-syntax expr-returning
  (syntax-rules ()
    ((expr-returning (car x)) '(car (expr-returning x)))
    ((expr-returning (cdr x)) '(cdr (expr-returning x)))
    ((expr-returning (cons x y)) '(cons (expr-returning x) (expr-returning y)))
    ((expr-returning obj) (cond [(null? obj)  'null]
                                [(number? obj)  'obj]
                                [(char? obj)  'obj]
                                [(boolean? obj) 'obj]
                                [(pair? obj) '(cons (expr-returning (car obj)) (expr-returning (cdr obj)))] ; expanded 
                                [else 'error]
                                ))))

因为d 是宏的符号,所以它是obj,在运行时它会变成文字表达式:

'(cons (expr-returning (car d)) (expr-returning (cdr d)))

评估它会触发 expr-returning 的扩展,因此您实际上正在评估 (cons '(car (expr-returning d)) '(cdr (expr-returning d))),它变成值 ((car (expr-returning d)) . (cdr (expr-returning d))),显示为 ((car (expr-returning d)) cdr (expr-returning d))

如果您要再次对此进行评估,请注意您的(expr-returning d) 确实会与以前一样。实际上,您实际上永远不会得到d 的结构,而是遵循d 作为一对的奇怪循环,然后遵循常量,然后回到d 作为一对。

创建一个过程很简单,该过程接受一个结构并生成一个表达式,该表达式将评估为原始结构的 equal?,只要您保持结构中没有像 @ 这样的文字版本987654333@ 或#<struct:bla>。这是一个只支持对和数字:

(define (obj->literal obj)
  (define (primitive? v)
    (or (number? v) (null? v)))
  (list 'quote 
        (let recur ((obj obj))
          (if (primitive? obj)
              obj
              (cons (recur (car obj)) 
                    (recur (cdr obj)))))))

(define e (obj->literal d))
e
; ==> (cons (cons 'a (cons 'e (cons 'i (cons 'o (cons 'u 'y))))) (cons 'i (cons 'o (cons 'u 'y))))
(equal? d (eval e (make-base-namespace)))
; ==> #t

【讨论】:

  • 糟糕。我在我的代码中犯了一个错误。循环不应该在那里。您的代码是正确的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
  • 1970-01-01
相关资源
最近更新 更多