【问题标题】:How to increment a Record Field using Scheme define-syntax macro如何使用 Scheme 定义语法宏增加记录字段
【发布时间】:2021-04-08 10:59:16
【问题描述】:

给定一个 Chez Scheme 记录,其中包含许多数字字段,这些字段不断地被小增量和减量(通常是一个)改变,有没有办法编写一个可以通过将字段值传递给字段值来改变字段值的宏?我现在完成此操作的方式类似于以下 REPL 成绩单:

Chez Scheme Version 9.5.4
Copyright 1984-2020 Cisco Systems, Inc.

> (define-record-type r (fields (mutable x) (mutable y)
                                ;; and so on...
                                ))
> (define my-r (make-r 3 5
                       ;; and so on...
                       ))
> (r-x-set! my-r (+ (r-x my-r) 1))
> my-r
#[#{r gak6l6ll8wuv7yd61kiomgudo-2} 4 5]

最好有一个简单的宏,比如inc!,它可以对记录中的字段执行变异递增/递减操作。我从类似 Lisps incfdecf 的 Scheme 版本开始,

(define-syntax inc!
    (syntax-rules ()
      ((_ x) (begin (set! x (+ x 1)) x))))

(inc! (r-x my-r)) ;; Syntax error

这适用于“普通”变量(并且易于实现dec!),但它不使用设置可变记录字段的机制,在本例中为r-x-set!

有没有明显的方法来编写这样的宏?您可以只传递对记录字段的引用,而不必为每个字段编写不同的内容?

【问题讨论】:

    标签: macros scheme chez-scheme define-syntax


    【解决方案1】:

    您可以从给定的访问器构造一个-set! mutator。这可以通过将访问器的符号转换为字符串并将"-set!" 附加到它来完成。然后eval 可以用来获取实际的mutator 过程。这是一个将指定字段增加一定数量n的宏:

    (define-syntax increment-n!
      (syntax-rules ()
        [(_ (acc rec) n)
         (let* ((acc-name (symbol->string (quote acc)))
                (mut-name (string-append acc-name "-set!"))
                (mut! (eval (string->symbol mut-name))))
           (mut! rec (+ (acc rec) n)))]))
    

    这可用于创建inc! 宏:

    (define-syntax inc!
      (syntax-rules ()
        [(_ (acc rec)) (increment-n! (acc rec) 1)]))
    

    但是,能够同时增加多个字段会很好;这里有 inc!dec! 可以做到这一点的宏:

    (define-syntax inc!
      (syntax-rules ()
        [(_ (acc rec) ...) (begin (increment-n! (acc rec) 1) ...)]))
    
    (define-syntax dec!
      (syntax-rules ()
        [(_ (acc rec) ...) (begin (increment-n! (acc rec) -1) ...)]))
    

    示例交互:

    > my-r
    #[#{r n5an6pxs3wvid36v2gvn8z9zo-5} 3 5 7]
    > (inc! (r-x my-r))
    > my-r
    #[#{r n5an6pxs3wvid36v2gvn8z9zo-5} 4 5 7]
    > (dec! (r-z my-r))
    > my-r
    #[#{r n5an6pxs3wvid36v2gvn8z9zo-5} 4 5 6]
    > (inc! (r-x my-r) (r-y my-r) (r-z my-r))
    > my-r
    #[#{r n5an6pxs3wvid36v2gvn8z9zo-5} 5 6 7]
    

    eval的使用注意事项

    increment-n! 宏构造了一个已经绑定到 mutator 过程的符号。然后可以将该符号直接绑定到 mut!,但是当计算表达式 (mut! rec (+ (acc rec) n)) 时,将引发异常,因为 mut! 现在计算为符号,例如 r-x-set!。我们希望mut! 在过程调用中评估为过程。通过首先在构造符号上调用eval,我们得到绑定到该符号的mutator 过程,将其绑定到mut! 而不是符号。

    这是一个说明问题的 REPL 交互,希望有助于澄清:

    > (define f (string->symbol "+"))
    > f
    +
    > (f 1 2)
    
    Exception: attempt to apply non-procedure +
    Type (debug) to enter the debugger.
    > (define f (eval (string->symbol "+")))
    > f
    #<procedure +>
    > (f 1 2)
    3
    

    【讨论】:

    • 谢谢。正是我所追求的。并且很容易通过 1 以外的值扩展到 inc/dec。如果您有时间,您能解释一下为什么在创建 mutator 时需要 eval 吗?我知道符号不是过程,但是eval 是如何解决这个问题的?
    • @clartaq - 我在答案的末尾添加了一些评论;如果您需要更多说明,请告诉我。
    • 这很有帮助。感谢您的补充说明。
    猜你喜欢
    • 2011-09-16
    • 2014-03-23
    • 1970-01-01
    • 2016-05-07
    • 2014-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多