我可以将多个变量一起定义如下:
(match-define (list a b c) (list 1 2 3))
a
b
c
输出:
1
2
3
>
但是我怎样才能在程序的后期再次重新定义这些变量?
基本上,我正在寻找一个匹配重新定义的'功能或宏。
以下不起作用:
(set! (list a b c) (list 10 20 30))
set!: not an identifier in: (list a b c)
我也试过了map功能:
> (map set! (list a b c) (list 5 6 7))
. set!: bad syntax in: set!
>
我是否需要为其编写函数或是否有一些简单的内置方法?
编辑:我尝试使用match-redefine宏,写在https://docs.racket-lang.org/guide/pattern-macros.html上交换宏的行上:
(match-define (list a b c) (list 1 2 3))
(println "------- sl --------")
(define sl (list a b c))
(println sl)
(println "------- vl --------")
(define vl (list 5 6 7))
(println vl)
(define-syntax-rule (match-redefine slist vlist)
(for ((i slist) (j vlist))
(set! i j)
)
)
(println "----- expecting sl to become 5 6 7 ----------")
(match-redefine sl vl)
(println sl)
但它不起作用。输出是:
"------- sl --------"
'(1 2 3)
"------- vl --------"
'(5 6 7)
"----- expecting sl to become 5 6 7 ----------"
'(1 2 3)
编辑:我发现match-let允许使用列表重新分配:
(define a 1)
(define b 2)
(define c 3)
(match-let (( (list a b c) (list 100 200 300) ))
(println a)
(println b)
(println c)
)
输出:
100
200
300
但它不是定义'或者设置!'用于所有剩余的源文件。
答案 0 :(得分:3)
问题是"在Racket"中重新定义变量列表。 答案是,你不能!
您可以拥有值列表,但不能包含变量列表。
表达式(list a b c)
计算为三个元素的列表:
> (define a 1)
> (define b 2)
> (define c 3)
> (list a b c)
(list 1 2 3)
在Racket中,您可以为值指定名称:
> (define xs (list 1 2 3))
> xs
(list 1 2 3)
现在变量xs
是指一个值(三个数字的列表)。
您可以使用set!
更改变量引用的内容:
> (set! xs (list 5 6 7))
(list 5 6 7)
如果您想同时更改三个变量,可以使用set-values!
。
> (set!-values (a b c) (values 8 9 10))
> a
8
名称a
,b
和c
是标识符 - 程序中出现的名称。
这意味着你可以不将它们放在列表中。
由于符号,人们可能会感到困惑:
> '(a b c)
'(a b c)
此处名称a,b,c
不是变量或标识符。它们是符号。
字符串列表的打印方式如下:
> '("a" "b" "c")
'("a" "b" "c")
另一方面,符号打印时没有任何字符。
如果您有一个符号a
,并且想要设置名称为a
的变量,则可以使用eval
- 但使用eval
是非常罕见的好主意。我会留下给你写谷歌的评论" eval是邪恶的球拍"。
编辑:添加了缺失的。
答案 1 :(得分:3)
@soegaard是绝对正确的。列表是值,它们包含其他值,而不是变量。
但是,如果我正确地阅读你的问题,你可能只需要一个很好的语法来进行模式匹配,根据它们在模式中出现的位置来改变一些变量,例如:
> (define a 1)
> (define b 2)
> (define c 3)
> (match-set! (list a b c) (list 10 20 30))
> a
10
> b
20
> c
30
可以使用在构造的local-expand
表单上执行match-define
的宏来实现这一点。这有点依赖于match-define
的内部实现细节,所以我不建议在实际代码中使用它。
这个宏必须进行一些额外的编译时操作,将define-values
(match-define
将扩展为)转换为set!-values
,因此在其中编写辅助函数是个好主意。 begin-for-syntax
进行转换。
#lang racket
(require syntax/parse/define
(for-syntax syntax/parse ; for syntax-parse
syntax/stx)) ; for stx-map
(begin-for-syntax
;; defs->set!s : Syntax -> Syntax
;; A helper function for match-set!, to transform define-values into set!-values
(define (defs->set!s defs)
(syntax-parse defs #:literals (begin define-values)
[(define-values [x ...] expr)
#'(set!-values [x ...] expr)]
[(begin defs ...)
#:with (set!s ...) (stx-map defs->set!s #'(defs ...))
#'(begin set!s ...)])))
然后宏需要构建一个match-define
表单,然后使用local-expand
将其扩展为define-values
表单,并对这些表单使用defs->set!s
帮助函数。 / p>
(define-syntax-parser match-set!
[(match-set! pat expr)
#:with defs
(local-expand #'(match-define pat expr) (syntax-local-context) #f)
(defs->set!s #'defs)])
使用它就像上面的例子一样。
答案 2 :(得分:2)
使用define-values
和set!-values
设置和更新多个变量:
(define-values (a b c) (values 1 2 3))
(set!-values (a b c) (values 2 3 4))
match
不应该被用作设置变量的一般方法,而是一种匹配结构并将其结构化为绑定的方法。
map
将无法工作,因为它发生在运行时而不是编译时。如果你引入自己的变量类型,你可以破解它。
#lang racket
(require srfi/26)
(define-values (runtime-bounded? runtime-value? runtime-set! runtime-get runtime-set-values! runtime-get-values)
(let ((vars (make-hasheq)))
;; a unique value to be our miss
(define fail (list "empty"))
;; check if we have a symbol
(define (bounded? name)
(hash-has-key? vars name))
;; check if a value is not our miss
(define (value? val)
(not (eq? fail val)))
;; single operations
(define (set! name val)
(hash-set! vars name val))
(define (get name (default fail))
(hash-ref vars name default))
;; list operations
(define (set-values! names values)
(for-each set! names values))
(define (get-values names (default fail))
(map (cut get <> default) names))
;; bind them to global symbols
(values bounded? value? set! get set-values! get-values)))
它的工作原理如下:
(define lst-names '(a b c))
(define lst-values '(1 2 3))
(runtime-set-values! lst-names lst-values)
(runtime-get-values '(a b c)) ; (1 2 3)
(runtime-get 'a) ; ==> 1
(runtime-set! 'a 10)
(runtime-get 'x 1337) ; 1337
(runtime-get-values '(a x) #f) ; (10 #f)
;; use map/for-each to set something
(for-each (cut runtime-set! <> 10) '(x y z)) ; sets x y z to 10
这是平的,但通过一些调整,甚至可以制作这种模仿词汇结构。然后,人们可能会考虑parameters而不是。
性能会比使用实际变量更差,但在大多数情况下,你可能根本就没有。
答案 3 :(得分:1)
您可以使用set!-values。
(match-define (list a b c) (list 1 2 3))
(set!-values (a b c) (values 10 20 30))