Variables 如何改变Scheme中的值,但只使用纯函数范式
我正在尝试更改一些变量的值,但如果不离开函数范式,我就无法做到这一点,例如使用Variables 如何改变Scheme中的值,但只使用纯函数范式,variables,functional-programming,scheme,lisp,racket,Variables,Functional Programming,Scheme,Lisp,Racket,我正在尝试更改一些变量的值,但如果不离开函数范式,我就无法做到这一点,例如使用set。有什么办法可以做到吗 示例代码: (lambda () (let ((more a)) (set! a b) (set! b (+ more b)) 我想更改a取b的值,我想更改b取(+more b)的值,但使用纯函数范式,不设置修改变量的那一刻,您就脱离了函数范式。最后用bang(!)标记的过程本质上是过程性的,如果您打算编写纯功能性代码,应该避免使用它们 您可以
set代码>。有什么办法可以做到吗
示例代码:
(lambda ()
(let ((more a))
(set! a b)
(set! b (+ more b))
我想更改a
取b
的值,我想更改b
取(+more b)
的值,但使用纯函数范式,不设置代码>修改变量的那一刻,您就脱离了函数范式。最后用bang(!
)标记的过程本质上是过程性的,如果您打算编写纯功能性代码,应该避免使用它们
您可以调用另一个函数(如果编写循环,可能是同一个函数),将“变量”的新值作为参数传递。例如:
(define a 26)
(define b 16)
(define (print-new-values a b)
; the modified values exist
; only inside this procedure
(printf "a is ~s~n" a)
(printf "b is ~s~n" b))
(let ((more a))
; notice that the parameter a = b
; and that the parameter b = more + b
; we didn't reassign anything, instead
; the parameters got bound to new values
(print-new-values b (+ more b)))
=> a is 16
b is 42
以上代码的输出与您打算编写的完全相同,但没有使用set代码>。作为比较:
(define a 26)
(define b 16)
(let ((more a))
(set! a b)
(set! b (+ more b))
(printf "a is ~s~n" a)
(printf "b is ~s~n" b))
=> a is 16
b is 42
你不能这样做,但你可以做一些同等的事情。假设我有一个函数f1
,其中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])
...)))
这是同样的事情。)
因此,这段代码现在做的事情与原始代码完全相同,只是它是纯功能性的(好吧:只要省略号中的代码是,它可能不是,因为第一个块实际上只能产生副作用)
现在有一个聪明的地方:Scheme需要正确地进行尾部递归。这意味着必须通过实现消除尾部调用。在Scheme中,函数调用本质上是传递参数,正如著名的“Lambda the ultimate”论文中所述。最初对f2的函数调用变成匿名函数是尾部调用,因此必须消除。任何合理的方案实现都很可能将此代码转换为与具有赋值的原始代码相同或可能更好的代码
lambda the ultimate papers网站上的一条注释:该网站曾承载过这些文件的副本,但仍有许多链接,包括来自维基百科的链接,该网站已变成垃圾邮件:不要跟随这些链接(该网站的名称中包含“read”和“scheme”字样)。现在寻找它们的最佳地点似乎是伦敦。很烦人的是,它们很难找到,因为它们绝对是基础性的论文。有很多模式可以做到这一点,最著名的是单子。但这里有一种叫做“延续传递样式”的方法:
(define set/a/b
(lambda (a b return)
;; new/a <= b
;; new/b <= a+b
(return b (+ a b))))
(define new/values
(lambda (a b return)
(set/a/b a b return)))
(new/values 10 20
(lambda (new/a new/b)
(display new/a)(newline)
(display new/b)(newline)))
(定义集合/a/b
(lambda(a b报税表)
;新的/a有阴影是可行的
(let ((a 10)) (b 20))
(let ((b a) (a b))
(list a b))) ; ==> (20 10)
但是,如果希望能够具有完全的灵活性,可以使用对象:
(define (fib-node a b)
(cons a b))
(define (fib-b f)
(cdr f))
(define (fib-value f)
(car f))
(define (fib-next f)
(let ((b (fib-b f)))
(fib-node b (+ b (fib-value f)))))
(define fib-null (fib-node 0 1))
;; iterating Fibonacci numbers without setting anything
(let loop ((n 10) (cur fib-null) (acc '()))
(if (zero? n)
(reverse acc)
(loop (- n 1) (fib-next cur) (cons (fib-value cur) acc))))
; ==> (0 1 1 2 3 5 8 13 21 34)
您的示例代码甚至都没有编译。您至少应该努力提供工作代码。更改变量值的目标已经超出了“函数范式”。这看起来像一个-您实际上要解决什么问题?看起来很安全。新建/值
等于设置/a/b
,它只是传递a
b
,然后返回