是否临时覆盖Scheme let block中的全局定义函数?
假设我有以下功能:是否临时覆盖Scheme let block中的全局定义函数?,scheme,racket,lexical-scope,dynamic-scope,lexical-closures,Scheme,Racket,Lexical Scope,Dynamic Scope,Lexical Closures,假设我有以下功能: (define (g x) (f x)) (define (f x) (+ 1 x)) 我想用不同的f临时调用g。例如,类似这样的内容: (let ((f (lambda (x) (+ 2 x)))) (g 5)) 我希望上面的代码计算为7,但它不是。相反,它的计算结果是6,因为g在let的范围之外调用f 有没有一种方法可以做到这一点,而不必在let中重新定义g,也不必在let中内联g的整个定义?(实际上,g可能是一个非常大、复杂的函数)。我不确定您是否可以,但我绝不
(define (g x) (f x))
(define (f x) (+ 1 x))
我想用不同的f
临时调用g
。例如,类似这样的内容:
(let ((f (lambda (x) (+ 2 x))))
(g 5))
我希望上面的代码计算为7,但它不是。相反,它的计算结果是6,因为g
在let
的范围之外调用f
有没有一种方法可以做到这一点,而不必在
let
中重新定义g
,也不必在let
中内联g
的整个定义?(实际上,g
可能是一个非常大、复杂的函数)。我不确定您是否可以,但我绝不是方案专家
我意识到您试图在不重新定义let
中的g
的情况下实现这一点,但是:
(define (h f x) (f x))
(define (g x) (h f x))
(define (f x) (+ 1 x))
(let ((f (lambda (x) (+ 2 x))))
(h f 5))
这样,您就保留了当前调用的g
的行为。但是,如果您想暂时拥有不同的行为,可以调用h
还有一点需要澄清的代码:
(let ((f (lambda (x) (+ 2 x))))
(display (g 5)) ; 6
(newline)
(h f 5)) ; 7
您可以使用
g
中的可选参数从let
表达式传递f
(define (g x . args)
(if (null? args)
(f x)
((car args) x)))
及
您所要求的是动态绑定,而不是“f”的词汇绑定。R6RS和R7RS通过参数支持这一点。这将满足您的要求:
(define f (make-parameter (lambda (x) (+ 1 x))))
(define (g x) ((f) x))
(display (g 5))(newline)
(parameterize ((f (lambda (x) (+ 2 x))))
(display (g 5))(newline))
我找到了一个做我想做的事情的方法,尽管我有很多人不会考虑这个犹太教:< /P>
(define (g x) (f x))
(define (f x) (+ 1 x))
(let ((old-f f))
(set! f (lambda (x) (+ 2 x)))
(let ((ans (g 5)))
(set! f old-f)
ans))
; -> 7
(g 5) ; -> 6
edit在回应下面的评论时,我甚至不知道fluid let是一件事。它甚至已经在麻省理工学院的计划中发挥了作用。这正是我需要的。如果下面的评论者发布类似于此的答案,则将成为公认的答案:
(define (g x) (f x))
(define (f x) (+ 1 x))
(fluid-let ((f (lambda (x) (+ x 2))))
(g 5)) ; -> 7
(g 5) ; -> 6
这是否仍然需要我将
g
的整个定义粘贴到let
中?我真正关心的情况是,g
是一个非常长、复杂的函数,可以多次调用f
。这个答案能处理那个案子吗?不,我不这么认为。我假设f
和g
是全局定义的?我的建议是创建一个新的过程h
,并将g
的主体剪切/粘贴到h
,其中h
将过程f
作为参数。然后,重新定义g
,以便调用h
,并传入全局定义的f
。因此,g
的行为保持不变,但只要您想用不同的f
调用g
,就可以调用h
并传入不同的f
。您可以向g
添加一个参数:(定义(gfx)(fx))
。然后(gf5)
将是6或7,这取决于通过的f
。您要求的是动态范围,这在20世纪60年代被发现不是一个很好的主意。从那以后,词汇范围一直统治着世界。这可以打包成一个宏,我不知道,比如什么。当然,我们需要增加对错误的保护,因此无论发生什么情况,f
的旧定义都会得到恢复,就像在CL中一样。那么继续/跳远呢?但作为一个简单的用户级实用程序,为什么不呢?fluid let
正是我想要的;请随意发布一个答案,我会点击acceptor,当然你可以用set来做!和全局变量,但您必须实现代码,以防止异常或调用continuation对象使全局变量处于不一致的状态,这也是线程安全的(另一方面,参数化是常见实现上的线程局部,一些流体实现也是如此)。你们把“球拍”包括在你们的团队中,球拍有参数化,但并没有(我相信)流体出租。更一般地说,parameterize在scheme报告中,fluid let不是。@WillNess是的,你是对的,事实上我一开始确实把Racket作为标签。不知道我为什么这么做,因为我几乎从未使用过它。
(define (g x) (f x))
(define (f x) (+ 1 x))
(fluid-let ((f (lambda (x) (+ x 2))))
(g 5)) ; -> 7
(g 5) ; -> 6