如何永久更改传递给 Racket 中的函数的参数值?

How can I permanently change the value of a parameter that was passed into a function in Racket?

我在更改传入参数的值时遇到问题。 set! 函数仅对函数外已定义的变量起作用。我希望我的函数更改与传入参数关联的值 而函数不返回任何内容 。 例如: 我有全局变量 deck1,我想创建一个函数,将列表的第一个元素添加到另一个列表,然后从第一个列表中删除该元素。像这样:

(define deck1 (list 1 2 3 4 5))
(define hand1 (list 6 7))    

(define (hit-card deck hand)                                                                                  
    (set! hand (append hand (first deck)))
    (set! deck (rest deck)))

如果我 运行 deck1 和 hand1 (hit-card deck1 hand1) 上的函数,我希望结果是

deck1: 1 2 3 4 5 ==> 2 3 4 5

hand1: 6 7 ==> 6 7 1

你不能,想要这样做几乎总是一种混淆的迹象:Racket 是一种 mostly-functional 语言,它希望你编写的程序的组成部分是函数,而不是带有任意 side-effects。如果函数的参数绑定到一个可变对象,您 可以 改变该对象。列表(以及更普遍的由 conses 构成的结构)在 Racket 中通常是不可变的。

我想你想要的默认是call-by-reference, but Racket is call-by-value

在 Racket 中通常有两种方法可以解决这个问题。

  1. 通过装箱和拆箱模拟call-by-reference
#lang racket

(define deck1 (box (list 1 2 3 4 5)))
(define hand1 (box (list 6 7)) )

(define (hit-card deck hand)
  (set-box! hand (cons (first (unbox deck)) (unbox hand)))
  (set-box! deck (rest (unbox deck))))

(hit-card deck1 hand1)

(unbox deck1) ; => '(2 3 4 5)
(unbox hand1) ; => '(1 6 7)
  1. 创建一种通过宏嵌入程序的小型 call-by-reference 语言。
#lang racket

(define-syntax-rule (define-get/put-id id get put!)
    (define-syntax id
      (make-set!-transformer
       (lambda (stx)
         (syntax-case stx (set!)
           [id (identifier? (syntax id)) (syntax (get))]
           [(set! id e) (syntax (put! e))])))))

(define-syntax-rule (define-cbr (id arg ...) body ...)
  (begin
    (define-syntax id
      (syntax-rules ()
        [(id actual (... ...))
         (do-f (lambda () actual)
               (... ...)
               (lambda (v)
                 (set! actual v))
               (... ...))]))
    (define-for-cbr do-f (arg ...)
      ()
      body ...)))

(define-syntax define-for-cbr
  (syntax-rules ()
    [(define-for-cbr do-f (id0 id ...)
       (gens ...) body ...)
     (define-for-cbr do-f (id ...)
       (gens ... (id0 get put)) body ...)]
    [(define-for-cbr do-f ()
       ((id get put) ...) body ...)
     (define (do-f get ... put ...)
       (define-get/put-id id get put) ...
       body ...)]))

上面的程序创建了一个新的特殊形式 define-cbr 类似于 Racket 中的 define 但是是 call-by-reference.

(define deck1 (list 1 2 3 4 5))
(define hand1 (list 6 7))

(define-cbr (hit-card deck hand)
  (set! hand (cons (first deck) hand))
  (set! deck (rest deck)))

(hit-card deck1 hand1)

deck1 ; => '(2 3 4 5)
hand1 ; => '(1 6 7)

更多详情,请参阅https://docs.racket-lang.org/guide/pattern-macros.html