如何永久更改传递给 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 中通常有两种方法可以解决这个问题。
- 通过装箱和拆箱模拟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)
- 创建一种通过宏嵌入程序的小型 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
我在更改传入参数的值时遇到问题。 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 中通常有两种方法可以解决这个问题。
- 通过装箱和拆箱模拟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)
- 创建一种通过宏嵌入程序的小型 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