为何使用sapply循环时formals()无需names(),显式循环却需要?
names(),而sapply不需要? 先来看你完成的操作:你先获取了base包中所有函数的列表:
objs <- mget(ls("package:base"), inherits = TRUE) funs <- Filter(is.function, objs)
然后用sapply轻松算出了每个函数的参数数量,找到了参数最多的函数:
f_arg_length <- sapply(funs, function(x) length(formals(x))) f_arg_length[which.max(f_arg_length)]
但用显式循环的时候,你发现必须搭配names()才能让formals()正常工作:
max_fun_name <- "" max_fun <- 0 for(x in 1:length(funs)) { if (length(formals(names(funs[x]))) > max_fun) { max_fun <- length(formals(names(funs[x]))) max_fun_name <- names(funs[x]) } } max_fun_name max_fun
核心差异其实出在你循环里的列表索引方式,以及sapply遍历列表的逻辑上:
1. funs的本质是命名列表
funs是一个命名列表:列表的每个元素是base包中的函数对象,而列表的names(funs)对应的就是这些函数的名字(比如"mean"、"lm"这类字符串)。
2. sapply的遍历逻辑
当你用sapply(funs, ...)时,sapply会自动遍历列表的每个元素本身——也就是直接把每个函数对象传递给匿名函数的参数x。所以x是实实在在的函数对象,formals(x)直接对函数对象调用,自然不需要额外的names()。
3. 你循环里的索引问题
你在循环里用的是funs[x](单括号索引),这返回的是一个长度为1的子列表,而不是单个函数对象!比如funs[1]返回的是list(abs = function(x) .Primitive("abs"))这样的结构,而不是abs函数本身。
这时候你调用names(funs[x]),得到的是这个子列表的名字(也就是函数名字符串,比如"abs"),而formals()函数很灵活:它既可以接受函数对象,也可以接受函数名的字符串(此时它会去查找这个名字对应的函数)。所以你的代码能工作,但其实是绕了个弯。
正确的循环写法(不需要names())
如果你用双括号索引funs[[x]],就能直接拿到单个函数对象,这时候就和sapply里的x一样了,不需要names():
max_fun_name <- "" max_fun <- 0 for(x in seq_along(funs)) { # 用seq_along比1:length(funs)更安全 current_fun <- funs[[x]] current_arg_count <- length(formals(current_fun)) if (current_arg_count > max_fun) { max_fun <- current_arg_count max_fun_name <- names(funs)[x] } } max_fun_name max_fun
总结一下:
sapply遍历的是列表的每个元素(函数对象),所以直接用formals(x)即可。- 你最初的循环用了单括号索引,得到的是子列表而非函数对象,所以只能通过
names()取到函数名字符串传给formals();而用双括号索引拿到函数对象后,就不需要names()了。
内容的提问来源于stack exchange,提问作者Narin Dhatwalia

