如何使用 Scheme define-syntax 宏增加记录字段
How to increment a Record Field using Scheme define-syntax macro
给定一个包含许多数字字段的 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!
,它可以对记录中的字段执行变异 increment/decrement 操作。我从 Lisps incf
和 decf
,
的 Scheme 版本开始
(define-syntax inc!
(syntax-rules ()
((_ x) (begin (set! x (+ x 1)) x))))
(inc! (r-x my-r)) ;; Syntax error
它适用于“普通”变量(并使其易于实现 dec!
),但它不使用设置可变记录字段的机制,在本例中为 r-x-set!
。
有没有明显的方法来编写这样的宏?您可以在其中传递对记录字段的引用,而不必为每个字段编写不同的内容?
您可以从给定的访问器构造一个 -set!
增变器。这可以通过将访问器的符号转换为字符串并向其附加 "-set!"
来完成。然后 eval
可用于获取实际的增变器过程。这是一个将指定字段递增一定数量的宏 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!
宏构造一个已绑定到增变过程的符号。然后可以将该符号直接绑定到 mut!
,但是当计算表达式 (mut! rec (+ (acc rec) n))
时会引发异常,因为 mut!
现在计算为一个符号,例如 r-x-set!
.我们希望 mut!
计算过程调用中的过程。通过首先在构造的符号上调用 eval
,我们得到绑定到该符号的增变过程,将其绑定到 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
给定一个包含许多数字字段的 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!
,它可以对记录中的字段执行变异 increment/decrement 操作。我从 Lisps incf
和 decf
,
(define-syntax inc!
(syntax-rules ()
((_ x) (begin (set! x (+ x 1)) x))))
(inc! (r-x my-r)) ;; Syntax error
它适用于“普通”变量(并使其易于实现 dec!
),但它不使用设置可变记录字段的机制,在本例中为 r-x-set!
。
有没有明显的方法来编写这样的宏?您可以在其中传递对记录字段的引用,而不必为每个字段编写不同的内容?
您可以从给定的访问器构造一个 -set!
增变器。这可以通过将访问器的符号转换为字符串并向其附加 "-set!"
来完成。然后 eval
可用于获取实际的增变器过程。这是一个将指定字段递增一定数量的宏 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!
宏构造一个已绑定到增变过程的符号。然后可以将该符号直接绑定到 mut!
,但是当计算表达式 (mut! rec (+ (acc rec) n))
时会引发异常,因为 mut!
现在计算为一个符号,例如 r-x-set!
.我们希望 mut!
计算过程调用中的过程。通过首先在构造的符号上调用 eval
,我们得到绑定到该符号的增变过程,将其绑定到 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