【问题标题】:Redefining a list of variables in Racket重新定义 Racket 中的变量列表
【发布时间】:2016-08-09 03:43:19
【问题描述】:

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

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

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

【问题讨论】:

    标签: scheme racket


    【解决方案1】:

    问题是“重新定义 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 is evil racket”这个短语。

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

    【讨论】:

    • 我们不能用宏来做吗?此页面上的交换如何工作docs.racket-lang.org/guide/pattern-macros.html
    • 也许是一个新问题,您可以在其中解释大局?
    • swap 这样的宏可以工作,因为宏不处理值,它们处理语法。像set! 这样的形式也不处理值,像match-define 这样的宏也不处理。 match-define 表单中的 (list a b c) 不是值。对match-define 来说,这是一段恰好看起来像list 函数调用的语法。
    【解决方案2】:

    @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
    

    可以做到这一点的,使用在构造的match-define 表单上执行local-expand 的宏。这在某种程度上取决于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 辅助函数。

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

    使用它的工作方式与上面的示例完全相同。

    【讨论】:

    • 我找到了那套! (also match-let) 与 (list a b c) 一起使用,但如果此类列表已命名,则不能。例如,以下不起作用:(define alist (list a b c)) (match-set!alist (list 10 20 30))。虽然 alist 的值为 (list 10 20 30),但 a b 和 c 保持不变。这可以通过你优雅的编码来纠正吗!
    • 我想我已经在不同的评论中告诉过你,但是match 表单中的(list a b c) 根本不是一个值。 x(set! x 5) 中也不是(list a b c)match-set! 形式中。如果您写了(set! alist (list 10 20 30)),您会期望abc 发生变化吗?
    • 如果您还没有阅读过,您应该阅读 Racket 文档的 Top-Level VariablesObjects and Imperative UpdateLocal Variables 部分,其中有很好的示例来解释这些概念
    【解决方案3】:

    一次使用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

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

    【讨论】:

      【解决方案4】:

      您可以使用set!-values

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

      【讨论】:

      • 我有 2 个列表: (list a b c) 和 (list 10 20 30) 。如何将它们转换为 (a b c) 和 (值 10 20 30)?
      猜你喜欢
      • 2015-04-08
      • 1970-01-01
      • 1970-01-01
      • 2013-03-10
      • 1970-01-01
      • 2019-05-27
      • 2012-12-15
      • 2020-04-04
      相关资源
      最近更新 更多