重新定义球拍中的变量列表

Redefining a list of variables in Racket

我可以定义多个变量,如下:

(match-define (list a b c) (list 1 2 3))
a
b
c

输出:

1
2
3
> 

但是我怎样才能在程序后面重新定义这些变量呢?

本质上,我正在寻找 'match-redefine' 函数或宏。

以下无效:

(set! (list a b c) (list 10 20 30))
set!: not an identifier in: (list a b c)

我也试过地图功能:

> (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

但它不是用于所有剩余源文件的 'define' 或 'set!'。

您可以使用 set!-values.

(match-define (list a b c) (list 1 2 3))
(set!-values (a b c) (values 10 20 30))

问题是"Redefining a list of variables in 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是个好主意。我将 google 短语 "eval is evil racket".

留给你

编辑:添加了一个缺失的 not。

@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 on a constructed match-define form. This is somewhat dependent on the internal implementation details of match-define 的宏,所以我不建议在实际代码中使用它。

此宏必须执行一些额外的编译时操作转换 define-values (which match-define will expand into) into set!-values, so it's a good idea to write a helper function within a 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 form, then use local-expand to expand that into the define-values 表单,并在这些表单上使用 defs->set!s 辅助函数。

(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)])

使用它与上面的示例完全一样。

一次性设置和更新多个变量 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

性能会比使用实际变量差,但在大多数情况下你可能根本不会那样。