R:装饰器功能的环境图
我想为以下代码绘制一个环境图,其中包含一个错误,以了解评估函数时如何精确工作。
# emphasize text
emph <- function(f, style = '**') {
function(...) {
if (length(style) == 1) {
paste(style, f(...), style)
} else {
paste(style[1], f(...), style[2])
}
}
}
# function to be decorated
tmbg <- function() {
'tmbg are okay'
}
# a decorator function with self-referencing name
tmbg <- emph(tmbg)
在评估装饰函数的呼叫表达式时,我遇到了错误
tmbg()
> Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
,我可以理解这与R中的功能参数的懒惰评估有关f
在返回的匿名函数中使用的全球帧中再次绑定到tmbg
,它再次返回匿名函数并调用f
,从而导致无限递归呼叫。但是,这个图像对我来说并不清楚,因为我不完全知道R中使用的评估模型是什么,尤其是在这种“懒惰评估”中。
下面我绘制了环境图的基本部分,并解释了等效代码 python 中使用的评估规则。 r。中使用的环境模型的清晰度相同
# This is the equivalent python code
def emph(f, style = ['**']):
def wrapper(*args):
if len(style) == 1:
return style[0] + f(*args) + style[0]
else:
return style[0] + f(*args) + style[1]
return wrapper
def tmbg():
return 'tmbg are okay'
tmbg = emph(tmbg)
tmbg()
我希望也能为R获得此类环境图,或者至少在评估第12行tmbg = emph时,在 (TMBG)
,需要首先评估Call Expression EMPH(TMBG)
。在评估呼叫表达式的操作员时,其形式参数f
在全局帧中绑定到名称tmbg
,该框架与我们在全局帧中定义的函数结合,如图所示以下。
接下来,在完成呼叫表达式emph(tmbg)
的评估后,其返回的函数wrapper
绑定到名称tmbg < /代码>在全球框架中。但是,
f
的绑定和实际函数tmbg
仍然保存在由emph
创建的本地帧中(f1
在下图中)。
因此,当在全球帧中评估tmbg()
时,不会有任何混乱的装饰函数(tmbg
in Global),哪些是要装饰的函数(f
在本地框架中)。与R相比,这是不同的部分,
看起来R的作用是它从f
- &gt; 函数tmbg()
to f
- &gt;名称tmbg
在全球帧中,它再次绑定到函数包装器(*args)
调用f
本身,从而导致此无限递归。但是,它也可能是一个完全不同的模型,r实际上并未真正将f
绑定到任何对象,而是名称tmbg
,而忽略了该名称所代表的内容。当它开始评估时,它会寻找名称tmbg
,并找到了由tmbg&lt; - emph(tmbg)
创建的全局,并获得了无限递归。但是,这听起来确实很奇怪,因为函数调用创建的本地范围不再计数(或部分计数),以便“懒惰评估”,一旦我们将表达式作为该函数的参数作为参数。除了由函数调用管理命名空间和示波器创建的环境之外,必须有一个运行的系统。
无论哪种情况,我都不清楚环境模型和评估规则R。我希望在这些模型上清楚并为R代码绘制一个环境图,如下所示。
I want to draw an environment diagram for the following code which contains an error to understand how R works exactly when evaluating a function.
# emphasize text
emph <- function(f, style = '**') {
function(...) {
if (length(style) == 1) {
paste(style, f(...), style)
} else {
paste(style[1], f(...), style[2])
}
}
}
# function to be decorated
tmbg <- function() {
'tmbg are okay'
}
# a decorator function with self-referencing name
tmbg <- emph(tmbg)
I got error while evaluating the call expression of the decorator function
tmbg()
> Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
I could understand this is related to the lazy evaluation of function parameter in R. It feels like when evaluating tmbg()
in global frame, the name of f
used in the returned anonymous function binds again to tmbg
in global frame which again returns the anonymous function and calls f
, thus leads to infinite recursive calls. But this image is not so clear to me because I don't exactly know what is the evaluation model used in R especially with this "lazy evaluation".
Below I draw the essential parts of the environment diagrams and explain the evaluation rule used in Python for the equivalent code. I hope to get such environment diagrams for R as well, or at least get the same level of clarity for the environmental model used in R.
# This is the equivalent python code
def emph(f, style = ['**']):
def wrapper(*args):
if len(style) == 1:
return style[0] + f(*args) + style[0]
else:
return style[0] + f(*args) + style[1]
return wrapper
def tmbg():
return 'tmbg are okay'
tmbg = emph(tmbg)
tmbg()
When evaluating the assignment statement at line 12 tmbg = emph(tmbg)
, the call expression emph(tmbg)
needs to be evaluated first. When evaluating the operator of the call expression, its formal parameter f
binds to name tmbg
in global frame which binds to a function we defined in global frame, as shown in the picture below.
Next, after finishing the evaluation of the call expression emph(tmbg)
, its returned function wrapper
binds to the name tmbg
in global frame. However the binding of f
and the actual function tmbg
is still hold in the local frame created by emph
(f1
in the diagram below).
Therefore when evaluating tmbg()
in global frame, there won't be any confusion about which is the decorator function (tmbg
in global) and which is the function to be decorated (f
in local frame). This is the different part compared to R.
It looks like what R does is that it changes the binding from f
-> function tmbg()
to f
-> name tmbg
in global frame, which again binds to function wrapper(*args)
calling f
itself and thus leads to this infinite recursion. But it might also be a completely different model that R does not really bind f
to any object but a name tmbg
and ignores what that name represents. When it starts to evaluate, it looks for name tmbg
and it finds the global one which is created by tmbg <- emph(tmbg)
and gets infinite recursion. But this sounds really weird as the local scope created by the function call does not count anymore (or partially counts) for the purpose of "lazy evaluation" as soon as we pass an expression as argument of that function. There has to be then a system running parallelly other than the environments created by the function calls managing the namespaces and the scopes.
In either case, it is not clear to me the environmental model and evaluation rule R. I want to be clear on these and draw an environment diagram for the R code as clear as the one below if possible.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
问题是不了解环境。问题是了解懒惰的评估。
由于懒惰的评估,f只是一个承诺,直到运行匿名函数,到那时TMBG已重新定义。为了强制f在运行emph时进行评估,添加了标记的###强制语句以强制它。没有其他行会更改。
就环境而言,匿名函数从EMPH中获得F,并且在Emph F中是一个承诺,除非我们添加强制语句,否则在运行匿名函数之前不会在呼叫者中查找。
我们可以使用Pryr软件包查看承诺。
这导致此输出表明f最初是未评估的,但是在调用力后,它包含f的值。如果我们不使用强制,匿名函数将在第一个Promise_Info()输出中显示的状态中访问F,因此它所知道的只是符号TMBG以及在哪里寻找它(全局环境)。
The problem is not understanding environments. The problem is understanding lazy evaluation.
Due to lazy evaluation f is just a promise which is not evaluated until the anonymous function is run and by that time tmbg has been redefined. To force f to be evaluated when emph is run add the marked ### force statement to force it. No other lines are changed.
In terms of environments the anonymous function gets f from emph and in emph f is a promise which is not looked up in the caller until the anonymous function is run unless we add the force statement.
We can look at the promise using the pryr package.
which results in this output that shows that f is at first unevaluated but after force is invoked it contains the value of f. Had we not used force the anonymous function would have accessed f in the state shown in the first promise_info() output so all it would know is a symbol tmbg and where to look for it (Global Environment).