提问人:Masoud 提问时间:9/25/2022 最后编辑:Hack-RMasoud 更新时间:9/26/2022 访问量:74
R 中的闭包,如 Python
Closures in R like Python
问:
首先考虑以下 Python 代码,这些代码计算函数被调用的次数:
def counter(fn):
count = 0
def inner(*args, **kwargs):
nonlocal count
count +=1
print('Function {0} was called {1} times'.format(fn.__name__, count))
return fn(*args, **kwargs)
return inner
def add(a,b):
return a+b
def mult(a,b):
return a*b
add = counter(add)
mult = counter(mult)
add(1,2)
add(2,3)
mult(1,5)
#output
Function add was called 1 times
Function add was called 2 times
Function mult was called 1 times
现在我正在尝试在 R 中执行相同的方法,如下所示:
counter <- function(fn) {
cnt <- 0
inner <- function(...) {
cnt <<- cnt + 1
print(paste("Function", match.call(), "was called", cnt, "times\n"))
return(fn(...))
}
return(inner)
}
add <- function(a, b) a + b
mult <- function(a, b) a*b
cnt_add <- counter(add)
cnt_add(1, 4)
cnt_add(3, 9)
[1] "Function cnt_add was called 1 times\n"
[2] "Function 1 was called 1 times\n" #<---- !!!!!!!!!!!!!! L1
[3] "Function 4 was called 1 times\n" #<---- !!!!!!!!!!!!!! L2
[1] 5
[1] "Function cnt_add was called 2 times\n"
[2] "Function 3 was called 2 times\n" #<---- !!!!!!!!!!!!!! L3
[3] "Function 9 was called 2 times\n" #<---- !!!!!!!!!!!!!!
[1] 12
cnt_mult<-counter(mult)
cnt_mult(1,6)
[1] "Function cnt_mult was called 1 times\n"
[2] "Function 1 was called 1 times\n" #<---- !!!!!!!!!!!!!! L4
[3] "Function 6 was called 1 times\n" #<---- !!!!!!!!!!!!!! L5
[1] 6
a) 我期望“功能?被称为?次\n“但为什么要打印 L1,L2,L3,L4,L5?
b) 当我尝试时(就像在 python 中一样)
add <- counter(add)
add(3, 4)
我收到一个错误:错误:评估嵌套太深...。
c) 为了避免 b 中的错误,我尝试了以下操作,但仍然出现错误
cnt_add <- counter(add)
add <- cnt_add
add(6, 8)
我发现如果我调用cnt_add函数一次,就不会发生错误(控制台中的额外两行除外):
cnt_add <- counter(add)
cnt_add(1, 8)
[1] "Function cnt_add was called 1 times\n"
[2] "Function 1 was called 1 times\n"
[3] "Function 8 was called 1 times\n"
[1] 9
add <- cnt_add
add(6, 8)
[1] "Function add was called 2 times\n" "Function 6 was called 2 times\n"
[3] "Function 8 was called 2 times\n"
[1] 14
但是为什么“函数添加被调用了 2 次”,我调用了一次!为什么它需要至少一次调用才能工作?
如何解决这些问题?我不想要其他方法,因为这只是闭包的一种做法。
答:
a) 为您提供整个调用,而不仅仅是您调用的函数的名称。用它来获得它。但是给你一个字符串版本,你传递的内容,所以它可能更好。(我的选择给出的是原始函数名称,而不是修改后的函数名称。如果你想要修改的那个,请坚持下去。match.call
match.call()[[1]]
deparse(substitute(fn))
fn
match.call()[[1]]
b) 你被懒惰的评价咬了一口。调用 的定义 。问题是从不计算,所以它作为一个承诺,直到你第一次调用时需要它。但是在这一点上,定义已经改变,所以你得到了无限循环。使用强制确定承诺的价值。force(fn)
counter
counter
fn
add
add
force(fn)
counter<-function(fn){
force(fn)
name <- deparse(substitute(fn))
cnt <- 0
inner <- function(...){
cnt <<- cnt+1
print(paste("Function",name,"was called",cnt,"times\n"))
return(fn(...))
}
return(inner)
}
add <- function(a,b) a+b
mult <- function(a,b) a*b
add <-counter(add)
add(1,4)
#> [1] "Function add was called 1 times\n"
#> [1] 5
add(3,9)
#> [1] "Function add was called 2 times\n"
#> [1] 12
创建于 2022-09-25 with reprex v2.0.2
使用它更相似。 可能更适合这种输出。归功于@user2554330,我把它包裹起来。sprintf
message
match.call()[[1]]
as.character()
counter <- function(fn) {
cnt <- 0
inner <- function(...) {
cnt <<- cnt + 1
message(sprintf("Function %s was called %s times", as.character(match.call()[[1]]), cnt))
return(fn(...))
}
return(inner)
}
add <- function(a, b) a + b
mult <- function(a, b) a*b
cnt_add <- counter(add)
cnt_add(1, 4)
# Function cnt_add was called 1 times
# [1] 5
cnt_add(3, 9)
# Function cnt_add was called 3 times
# [1] 12
评论
counter
fn
cnt_add
add
force(fn)
deparse(substitute))
message(sprintf(.))
fname
存储函数名称。 查找传递给 的函数。get(fname, mode="function")
counter
counter = function(fn) {
i = 0
fname = deparse(substitute(fn))
fn = get(fname, mode="function")
function(...) {
i <<- i+1
cat(sprintf("Function %s was called %d times", fname, i), "\n")
return(fn(...))
}
}
add = function(a, b) a+b
add = counter(add)
add(1, 6)
Function add was called 1 times
[1] 7
add(7, 6)
Function add was called 2 times
[1] 13
mult = function(a, b) a*b
mult = counter(mult)
mult(2, 3)
Function mult was called 1 times
[1] 6
mult(5, 3)
Function mult was called 2 times
[1] 15
评论
fn
add <- counter(function(a, b) a + b)
fn
get()
评论