R:装饰器功能的环境图
R: environment diagram for decorator function
我想为以下代码绘制一个环境图,其中包含错误以了解 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中函数参数的惰性评估有关。感觉在全局框架中评估tmbg()
时,返回的匿名函数中使用的f
的名称绑定再次在全局框架中 tmbg
再次 returns 匿名函数并调用 f
,从而导致无限递归调用。但是这张图片对我来说不是很清楚,因为我不完全知道 R 中使用的评估模型是什么,尤其是这种“惰性评估”。
下面画出环境图的主要部分,并说明Python中使用的求值规则等价代码。 我希望 R 也能得到这样的环境图,或者至少能得到 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()
在第 12 行 tmbg = emph(tmbg)
处计算赋值语句时,需要先计算调用表达式 emph(tmbg)
。在计算调用表达式的运算符时,它的形参f
绑定到全局框架中的名称tmbg
,它绑定到我们在全局框架中定义的函数,如下图所示。
接下来,完成调用表达式emph(tmbg)
的计算后,其返回函数wrapper
绑定到全局框架中的名称tmbg
。但是 f
和实际函数 tmbg
的绑定仍然保留在由 emph
创建的本地框架中(下图中的 f1
)。
因此在全局框架中计算tmbg()
时,不会混淆哪个是装饰器函数(全局tmbg
)哪个是要装饰的函数(f
在本地框架中)。这是与R不同的部分。
看起来 R 所做的是它在全局框架中将绑定从 f
-> function tmbg()
更改为 f
-> 名称 tmbg
,这又是绑定到 function wrapper(*args)
调用 f
本身,从而导致这种无限递归。但它也可能是一个完全不同的模型,R 并没有真正将 f
绑定到任何对象,而是一个名称 tmbg
并忽略该名称代表的内容。当它开始评估时,它会查找名称 tmbg
并找到由 tmbg <- emph(tmbg)
创建的全局名称并获得无限递归。但这听起来真的很奇怪,因为一旦我们将表达式作为该函数的参数传递,函数调用创建的局部作用域就不再(或部分地)用于“惰性求值”的目的。除了由管理命名空间和范围的函数调用创建的环境之外,还必须有一个并行的系统 运行。
无论哪种情况,我都不清楚环境模型和评估规则R。我想清楚这些,并为R代码画一个环境图,如果可能的话,如下图。
问题是不了解环境。问题是理解惰性求值。
由于惰性求值,f 只是一个承诺,直到匿名函数 运行 并且 tmbg 已被重新定义时才会被求值。要在 emph 为 运行 时强制计算 f,请添加标记的 ### force 语句以强制执行。没有更改其他行。
就环境而言,匿名函数从 emph 获取 f,在 emph 中,f 是一个承诺,除非我们添加 force 语句,否则在匿名函数 运行 之前不会在调用者中查找它。
emph <- function(f, style = '**') {
force(f) ###
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()
## [1] "** tmbg are okay **"
我们可以使用 pryr 包查看 promise。
library(pryr)
emph <- function(f, style = '**') {
str(promise_info(f))
force(f)
cat("--\n")
str(promise_info(f))
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'
}
tmbg <- emph(tmbg)
这导致此输出显示 f 最初未计算,但在调用 force 后它包含 f 的值。如果我们不使用强制,匿名函数将在第一个 promise_info() 输出中显示的状态下访问 f,因此它所知道的只是一个符号 tmbg 以及在哪里寻找它(全局环境)。
List of 4
$ code : symbol tmbg
$ env :<environment: R_GlobalEnv>
$ evaled: logi FALSE
$ value : NULL
--
List of 4
$ code : symbol tmbg
$ env : NULL
$ evaled: logi TRUE
$ value :function ()
..- attr(*, "srcref")= 'srcref' int [1:8] 1 13 3 5 13 5 1 3
.. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x00000000102c3730>
我想为以下代码绘制一个环境图,其中包含错误以了解 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中函数参数的惰性评估有关。感觉在全局框架中评估tmbg()
时,返回的匿名函数中使用的f
的名称绑定再次在全局框架中 tmbg
再次 returns 匿名函数并调用 f
,从而导致无限递归调用。但是这张图片对我来说不是很清楚,因为我不完全知道 R 中使用的评估模型是什么,尤其是这种“惰性评估”。
下面画出环境图的主要部分,并说明Python中使用的求值规则等价代码。 我希望 R 也能得到这样的环境图,或者至少能得到 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()
在第 12 行 tmbg = emph(tmbg)
处计算赋值语句时,需要先计算调用表达式 emph(tmbg)
。在计算调用表达式的运算符时,它的形参f
绑定到全局框架中的名称tmbg
,它绑定到我们在全局框架中定义的函数,如下图所示。
接下来,完成调用表达式emph(tmbg)
的计算后,其返回函数wrapper
绑定到全局框架中的名称tmbg
。但是 f
和实际函数 tmbg
的绑定仍然保留在由 emph
创建的本地框架中(下图中的 f1
)。
因此在全局框架中计算tmbg()
时,不会混淆哪个是装饰器函数(全局tmbg
)哪个是要装饰的函数(f
在本地框架中)。这是与R不同的部分。
看起来 R 所做的是它在全局框架中将绑定从 f
-> function tmbg()
更改为 f
-> 名称 tmbg
,这又是绑定到 function wrapper(*args)
调用 f
本身,从而导致这种无限递归。但它也可能是一个完全不同的模型,R 并没有真正将 f
绑定到任何对象,而是一个名称 tmbg
并忽略该名称代表的内容。当它开始评估时,它会查找名称 tmbg
并找到由 tmbg <- emph(tmbg)
创建的全局名称并获得无限递归。但这听起来真的很奇怪,因为一旦我们将表达式作为该函数的参数传递,函数调用创建的局部作用域就不再(或部分地)用于“惰性求值”的目的。除了由管理命名空间和范围的函数调用创建的环境之外,还必须有一个并行的系统 运行。
无论哪种情况,我都不清楚环境模型和评估规则R。我想清楚这些,并为R代码画一个环境图,如果可能的话,如下图。
问题是不了解环境。问题是理解惰性求值。
由于惰性求值,f 只是一个承诺,直到匿名函数 运行 并且 tmbg 已被重新定义时才会被求值。要在 emph 为 运行 时强制计算 f,请添加标记的 ### force 语句以强制执行。没有更改其他行。
就环境而言,匿名函数从 emph 获取 f,在 emph 中,f 是一个承诺,除非我们添加 force 语句,否则在匿名函数 运行 之前不会在调用者中查找它。
emph <- function(f, style = '**') {
force(f) ###
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()
## [1] "** tmbg are okay **"
我们可以使用 pryr 包查看 promise。
library(pryr)
emph <- function(f, style = '**') {
str(promise_info(f))
force(f)
cat("--\n")
str(promise_info(f))
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'
}
tmbg <- emph(tmbg)
这导致此输出显示 f 最初未计算,但在调用 force 后它包含 f 的值。如果我们不使用强制,匿名函数将在第一个 promise_info() 输出中显示的状态下访问 f,因此它所知道的只是一个符号 tmbg 以及在哪里寻找它(全局环境)。
List of 4
$ code : symbol tmbg
$ env :<environment: R_GlobalEnv>
$ evaled: logi FALSE
$ value : NULL
--
List of 4
$ code : symbol tmbg
$ env : NULL
$ evaled: logi TRUE
$ value :function ()
..- attr(*, "srcref")= 'srcref' int [1:8] 1 13 3 5 13 5 1 3
.. ..- attr(*, "srcfile")=Classes 'srcfilecopy', 'srcfile' <environment: 0x00000000102c3730>