如何在Scheme中更改值,但仅使用纯功能范例

Jas*_*inn 3 lisp variables scheme functional-programming racket

我正在尝试更改某些变量的值,但是如果不离开功能范例(例如使用),我将无法做到set!。有什么办法可以做到吗?

示例代码:

(lambda ()
      (let ((more a))
        (set! a b)
        (set! b (+ more b))
Run Code Online (Sandbox Code Playgroud)

我想改变a的价值,b我想改变b的价值,(+ more b)但使用纯粹的功能范式,没有set!

tfb*_*tfb 6

您不能执行此操作,但是可以执行等效操作。假设我有一些函数f1,其中ab被绑定(假设它们是参数,因为这会使事情变得更容易)。在某个时候我想交换ab。所以我从这个开始:

(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 ...))
Run Code Online (Sandbox Code Playgroud)

而且此代码显然不起作用,因为它具有赋值功能。但是我可以通过发明一个新功能来做到这一点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 ...))
Run Code Online (Sandbox Code Playgroud)

该代码功能性的,并且执行相同的操作。然后我就可以摆脱f2的名称,因为函数是一流的:

(define f1
  (? (a b)
    ... code that uses a and b ...
    ((? (a b)
       ... code that uses a and b, now swapped ...)
     b a))
Run Code Online (Sandbox Code Playgroud)

(显然,我们将其写为:

(define f1
  (? (a b)
    ...
    (let ([b a] [a b])
      ...)))
Run Code Online (Sandbox Code Playgroud)

这是同一件事。)

因此,这段代码现在的功能与原始代码完全相同,不同之处在于它纯粹是功能性的(嗯:只要省略号中的代码可以(可能不是),因为第一个块实际上只能并行执行某项操作-影响)。

现在这是一个聪明的地方:Scheme必须正确地尾部递归。这意味着必须通过实现消除尾调用。在Scheme中,函数调用本质上是GO TO传递自变量,如揭穿“昂贵的过程调用”神话,或“过程调用实现被认为有害”,或者Lambda:Ultimate GOTO(著名的Lambda最终版本)之一的论文。最初f2成为匿名函数的函数调用是尾部调用,因此必须消除。任何合理的Scheme实现都很可能会将这段代码转换为与具有赋值的朴素代码相同或更好的代码。


关于lambda-the-ultimate论文的注释:以前用来存放它们的副本的站点,但仍然有很多链接(包括来自Wikipedia的链接)已变成垃圾邮件:不要关注那些链接(该站点的名称包括“阅读”和“方案”。现在寻找它们的最佳地点似乎是MITAI Memos存储库。令人十分恼火的是,它们变得非常难找,因为它们绝对是基础论文。