R语言实现类Python闭包计数器:问题排查与修复
R闭包实现函数调用计数的问题修复
先看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) # 输出 # Function add was called 1 times # Function add was called 2 times # Function mult was called 1 times
你在R里复刻这个逻辑时遇到了三个问题:
- 打印出多余的参数相关行(比如
Function 1 was called 1 times) - 直接执行
add <- counter(add)后调用add(3,4)会触发递归深度错误 - 用临时变量中转后要么报错,要么计数异常
原R代码如下:
counter <- function(fn) { cnt <- 0 inner <- function(...) { cnt <<- cnt + 1 print(paste("Function", match.call(), "was called", cnt, "times")) 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) cnt_mult<-counter(mult) cnt_mult(1,6)
下面是修复后的代码,完全用闭包解决所有问题:
counter <- function(fn) { # 提前获取并保存原函数的名称,避免后续赋值后丢失 fn_name <- deparse(substitute(fn)) cnt <- 0 inner <- function(...) { cnt <<- cnt + 1 # 用预先保存的函数名输出,避免解析调用表达式带来的冗余 cat(sprintf("Function %s was called %d times\n", fn_name, cnt)) # 用do.call确保调用的是原函数,不是被替换后的闭包 do.call(fn, list(...)) } # 给闭包设置和原函数一致的名称,保持语义 attr(inner, "name") <- fn_name inner } # 测试,和Python逻辑完全一致 add <- function(a, b) a + b mult <- function(a, b) a*b add <- counter(add) add(1, 2) add(2, 3) mult <- counter(mult) mult(1, 5)
修复细节:
解决冗余打印:
原代码用match.call()会返回整个调用语句(包括参数),paste会把它拆成多个元素输出。现在在闭包创建时就用deparse(substitute(fn))拿到原函数的名字存起来,打印时直接用这个名字,再用cat+sprintf代替print(paste),输出更干净。解决递归死循环:
原代码里fn(...)会在add <- counter(add)后变成调用闭包自己,因为fn是对变量的引用,不是原函数对象。改用do.call(fn, list(...)),确保调用的是闭包创建时捕获的原函数,不会触发递归。解决计数异常和赋值问题:
闭包创建时就固定了原函数的引用和名称,不管后续怎么给变量赋值,闭包内部的逻辑都不受影响,不需要依赖临时变量提前调用,计数也能准确统计。
内容的提问来源于stack exchange,提问作者Masoud
相关产品推荐
相关产品推荐

