Racket:使用一个函数更改两个常量

3
我想使用一个函数同时更改两个常量,但不知何故它没有生效:
(define p 1)
(define q 1)

(define (change p q)
  (set! p (+ p 1))
  (set! q (+ q 1))) 

2
这个函数确实可以工作,只是它的工作方式不是你想象中的那样。发生的情况是你有两个绑定的p和两个绑定的q。这个函数改变了它的形式参数p和q的值,但不会改变全局范围内的p和q。 - WorBlux
4个回答

2

这样做是可行的:

#lang racket
(define p 1)
(define q 1)

(define (increment-p-and-q)
  (set! p (+ p 1))
  (set! q (+ q 1)))


(display (list p q)) ; displays (1 1)
(newline)
(increment-p-and-q)  ; mutates p and q
(display (list p q)) ; displays (2 2)
(newline)

当您调用与全局变量具有相同绑定的过程时,该变量将被隐藏。如果您执行 set! 操作,只有本地绑定会发生更改,不会更改全局绑定。


@JimBoy,我已经更新了代码并提供了完整的示例。这对你不起作用吗? - Sylwester
我一直收到以下错误信息:定义:函数主体应该只有一个表达式,但是找到了1个额外的部分。 - JimBoy
@JimBoy 我的代码可以在左下角使用“从源代码确定语言”和作为第一行的#lang racket正常工作,但是你没有使用它吗?如果你使用的是不同的语言(DrRacket支持数十种语言),你需要在问题中包含该信息,我们会更新我们的答案。 - Sylwester
谢谢,Sylwester。请参见上文。 - JimBoy

2

有可能您使用的语言在过程体内没有隐式的begin,让我们试着明确地编写它:

(define p 1) ; declare variables as global, so they 
(define q 1) ; can be modified inside a procedure

(define (change-vars) ; don't pass them, they won't get modified inside proc
  (begin ; use begin to evaluate expressions sequentially from left to right
    (set! p (+ p 1))
    (set! q (+ q 1)))) ; value of last expression is returned, here's #<void>

它按照预期工作:

p
=> 1
q
=> 1

(change-vars)

p
=> 2
q
=> 2

1
非常感谢你,Óscar!我之前不知道"begin"可以解决这个问题。 - JimBoy

1

@Sylwester给出了一个好的、直接的答案。另一个直接的答案是,如果你想让一个函数改变其参数(像C++中的引用参数一样),使用boxes。例如:

(define (change p)
  (set-box! p (+ (unbox p) 1)))

(define p (box 1))

(for/list ([i 3])
  (change p)
  (unbox p))
; => '(2 3 4)

然而,像这样使用变异在Racket中并不常见或被鼓励。虽然你可以这样做,但更加函数化的方法被鼓励,甚至可以允许Racket优化你的代码以实现更快的运行

两种解决方案的问题在于,它们可以很好地工作,如果一个人只想改变一个常量,但是如果一个人尝试改变两个常量,程序会崩溃。 - JimBoy

1
您可以使用宏(在此情况下是define-syntax-rule),这样您就可以将变量作为参数传递:
#lang racket

(define p 1)
(define q 1)

(define-syntax-rule (change p q)
  (begin
    (set! p (+ p 1))
    (set! q (+ q 1))))

(display (list p q)) ; displays (1 1)
(newline)
(change p q)  ; mutates p and q
(display (list p q)) ; displays (2 2)
(newline) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接