我正在尝试更改某些变量的值,但如果不离开函数范式我就无法做到这一点,例如使用 set!
。有什么办法可以做到吗?
示例代码:
(lambda ()
(let ((more a))
(set! a b)
(set! b (+ more b))
我想更改 a
取值 b
我想更改 b
取值 (+ more b )
但使用纯函数范式,没有 set!
。
最佳答案
你不能这样做,但你可以做一些等效的事情。假设我有一些函数 f1
,其中绑定(bind)了 a
和 b
(假设它们是参数,因为这会让事情变得更容易) .在某些时候,我想交换 a
和 b
。所以我从这个开始:
(define f1
(λ (a b)
... code that uses a and b ...
(let ([tmp a])
(set! a b)
(set! b tmp))
... code that uses a and b, now swapped ...))
这段代码显然不能正常工作,因为它里面有赋值。但我可以做到这一点,发明一个新函数 f2
:
(define f1
(λ (a b)
... code that uses a and b ...
(f2 b a)))
(define f2
(λ (a b)
... code that uses a and b, now swapped ...))
这段代码是功能性的,它做同样的事情。然后我可以去掉 f2
的名字,因为函数是一流的:
(define f1
(λ (a b)
... code that uses a and b ...
((λ (a b)
... code that uses a and b, now swapped ...)
b a))
(显然我们会这样写:
(define f1
(λ (a b)
...
(let ([b a] [a b])
...)))
这是一回事。)
所以这段代码现在做的事情和原来的代码完全一样,除了它是纯函数式的(好吧:只要省略号中的代码是,它可能不是,因为第一个 block 实际上只能做一些事情副作用)。
现在这是一个聪明的地方:要求方案是正确的尾递归。这意味着必须通过实现消除尾调用。在 Scheme 中,函数调用本质上是 GO TO 传递参数,如著名的 Debunking the 'Expensive Procedure Call' Myth, or, Procedure Call Implementations Considered Harmful, or, Lambda: The Ultimate GOTO 中所述。 ,著名的“Lambda the Ultimate”论文之一。对最初为 f2
并成为匿名函数的函数调用是尾调用,因此必须消除。任何合理的 Scheme 实现很可能会将此代码转换为与带有赋值的原始代码相同或可能更好的代码。
关于 lambda-the-ultimate 论文的注释:该站点曾经托管它们的副本并且仍然有许多链接,包括来自维基百科的链接已变成垃圾邮件:不要按照这些链接(该站点的名称包含“阅读”和“方案”两个词)。现在寻找它们的最佳地点似乎是 AI Memos repository at MIT .很烦人的是,它们已经变得如此难以找到,因为它们绝对是基础论文。
关于variables - 如何更改 Scheme 中的值但仅使用纯函数范式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56572927/