所以我正在改变我在R中构建的一些函数的类,以便添加一个描述属性,因为我想使用S3泛型来为我处理所有事情.基本上,我有一个像这样的结构
foo <- function(x) x + 1
addFunction <- function(f, description) {
class(f) <- c("addFunction", "function")
attr(f, "description") <- description
f
}
foo <- addFunction(foo, "Add one")
Run Code Online (Sandbox Code Playgroud)
然后我做的事情就像
description <- function(x) UseMethod("description")
description.default <- function(x) deparse(substitute(x))
description.addFunction <- function(x) attr(x, "description")
Run Code Online (Sandbox Code Playgroud)
这很好用,但不是那么优雅.我想知道是否可以定义一个新类的函数,以便可以用类似于function
语法的语法定义此类的实例.换句话说,是能够限定addFunction
,使得foo
在下面的方式产生的:
foo <- addFunction(description = "Add one", x) {
x + 1
}
Run Code Online (Sandbox Code Playgroud)
(或者类似的东西,我对属性应该添加到函数中的位置没有强烈的感觉)?
谢谢阅读!
更新:我已经尝试了更多这个想法,但还没有真正达到任何具体结果 - 所以这只是我对当前(更新)主题的概述:
我尝试了只复制function()
-function,给它一个不同的名字,然后再操作它的想法.但是,这不起作用,我想对这里发生的事情有任何意见:
> function2 <- `function`
> identical(`function`, function2)
[1] TRUE
> function(x) x
function(x) x
> function2(x) x
Error: unexpected symbol in "function2(x) x"
> function2(x)
Error: incorrect number of arguments to "function"
Run Code Online (Sandbox Code Playgroud)
作为function()
一个原始函数,我试着查看定义它的C代码以获得更多线索.我对来自function2(x)
呼叫的错误消息特别感兴趣.底层的C代码function()
是
/* Declared with a variable number of args in names.c */
SEXP attribute_hidden do_function(SEXP call, SEXP op, SEXP args, SEXP rho)
{
SEXP rval, srcref;
if (TYPEOF(op) == PROMSXP) {
op = forcePromise(op);
SET_NAMED(op, 2);
}
if (length(args) < 2) WrongArgCount("function");
CheckFormals(CAR(args));
rval = mkCLOSXP(CAR(args), CADR(args), rho);
srcref = CADDR(args);
if (!isNull(srcref)) setAttrib(rval, R_SrcrefSymbol, srcref);
return rval;
}
Run Code Online (Sandbox Code Playgroud)
从这个,我的结论是出于某种原因,至少有两个的四个参数call
,op
,args
和rho
现在要求.从do_function()
我的签名我猜测传递给的四个参数do_function
应该是一个调用,一个promise,一个参数列表,然后可能是一个环境.我尝试了很多不同的组合function2
(包括将这些参数中的两个设置为NULL),但我不断收到相同的(新)错误消息:
> function2(call("sum", 2, 1), NULL, list(x=NULL), baseenv())
Error: invalid formal argument list for "function"
> function2(call("sum", 2, 1), NULL, list(x=NULL), NULL)
Error: invalid formal argument list for "function"
Run Code Online (Sandbox Code Playgroud)
从C函数返回此错误消息CheckFormals()
,我也查找了:
/* used in coerce.c */
void attribute_hidden CheckFormals(SEXP ls)
{
if (isList(ls)) {
for (; ls != R_NilValue; ls = CDR(ls))
if (TYPEOF(TAG(ls)) != SYMSXP)
goto err;
return;
}
err:
error(_("invalid formal argument list for \"function\""));
}
Run Code Online (Sandbox Code Playgroud)
我根本不会说流利的C,所以从这里开始我不太清楚下一步该做什么.
所以这些是我更新的问题:
function
并function2
没有以同样的方式表现?为什么我function2
在R中认为它们相同时需要使用不同的语法?function2
,这样function2([arguments])
实际上会定义一个函数?R 中的一些关键字(例如if
和 )function
在调用底层函数时具有特殊语法。如果需要的话,它很容易用作if
函数,例如
`if`(1 == 1, "True", "False")
Run Code Online (Sandbox Code Playgroud)
相当于
if (1 == 1) {
"True"
} else {
"False"
}
Run Code Online (Sandbox Code Playgroud)
function
比较棘手。上一个问题对此有一些帮助。
对于您当前的问题,这是一种解决方案:
# Your S3 methods
description <- function(x) UseMethod("description")
description.default <- function(x) deparse(substitute(x))
description.addFunction <- function(x) attr(x, "description")
# Creates the pairlist for arguments, handling arguments with no defaults
# properly. Also brings in the description
addFunction <- function(description, ...) {
args <- eval(substitute(alist(...)))
tmp <- names(args)
if (is.null(tmp)) tmp <- rep("", length(args))
names(args)[tmp==""] <- args[tmp==""]
args[tmp==""] <- list(alist(x=)$x)
list(args = as.pairlist(args), description = description)
}
# Actually creates the function using the structure created by addFunction and the body
`%{%` <- function(args, body) {
stopifnot(is.pairlist(args$args), class(substitute(body)) == "{")
f <- eval(call("function", args$args, substitute(body), parent.frame()))
class(f) <- c("addFunction", "function")
attr(f, "description") <- args$description
f
}
# Example. Note that the braces {} are mandatory even for one line functions
foo <- addFunction(description = "Add one", x) %{% {
x + 1
}
foo(1)
#[1] 2
Run Code Online (Sandbox Code Playgroud)