重新定义Racket中的变量列表

时间:2016-08-09 03:43:19

标签: scheme racket

我可以将多个变量一起定义如下:

(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

但它不是定义'或者设置!'用于所有剩余的源文件。

4 个答案:

答案 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

名称abc是标识符 - 程序中出现的名称。 这意味着你可以将它们放在列表中。

由于符号,人们可能会感到困惑:

 > '(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-valuesmatch-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-valuesset!-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))