从函数返回列表中的函数

时间:2011-02-21 03:30:57

标签: function r

我搜索了这个问题,但找到了不够具体的答案。

我正在清理旧代码,我正在努力确保以下内容相对干净,并希望以后它不会在后面咬我。

我的问题是关于通过函数传递函数。查看以下绘图陈述中的“y”部分。 goo(df)[[1]](x)这件事有效,但我是否以任何方式要求麻烦?如果是这样,有更干净的方式吗?

此外,如果多次调用goo()函数,例如在蒙特卡罗分析中,这会加载R的内部结构还是可能导致某种类型的环境问题?

编辑(02/21/2011)---以下代码只是一个例子。真正的函数“goo”在进入approxfun()语句之前有很多代码。

#Build a dataframe
df <- data.frame(a=c(1, 2, 3, 4, 5), b=c(4, 3, 1, 2, 6))

#Build a function that passes a function
goo <- function(inp.df) {
  out.fun <- approxfun(x=inp.df$a, y=inp.df$b, yright=max(inp.df$b), method="linear", f=1)
  list(out.fun, inp.df$a[5], inp.df$b[5])
}

#Set up the plot range
x <- seq(1, 4.3, 0.01)

#Plot the function
plot(x, goo(df)[[1]](x), type="l", xlim=c(0, goo(df)[[2]]), ylim=c(0, goo(df)[[3]]), lwd=2, col="red")
grid()



goo(df)

[[1]]
function (v) 
.C("R_approxfun", as.double(x), as.double(y), as.integer(n), 
    xout = as.double(v), as.integer(length(v)), as.integer(method), 
    as.double(yleft), as.double(yright), as.double(f), NAOK = TRUE, 
    PACKAGE = "stats")$xout
<environment: 0219d56c>

[[2]]
[1] 5

[[3]]
[1] 6

3 个答案:

答案 0 :(得分:4)

如果不确切知道您的代码是什么,很难给出具体的建议,但这里有几点需要考虑:

  1. 是否真的有必要在其返回值中包含goo个输入数据?换句话说,你能让goo成为一个只返回功能的简单工厂吗?在您的示例中,至少plot函数已经具有确定限制所需的所有数据。
  2. 如果无法做到这一点,那么请保持这种模式,但是给出goo的返回值描述性名称的元素,这样至少可以很容易地看到引用它们时发生了什么。 (例如,goo(df)$approx(x)。)如果在您的代码中广泛使用此结构,请考虑将其作为S3类。
  3. 最后,不要在绘图函数中多次调用goo(df),只是为了获得不同的元素。当你这样做时,你每次都会调用goo,正如你所说的那样会执行很多代码。此外,每个调用都有自己的环境,其中包含输入数据的副本(尽管R将足够智能,可以在一定程度上减少复制并使用df的相同物理实例。)而是调用{{ 1}}一次,将其值赋给变量,然后引用该变量。

答案 1 :(得分:3)

我会删除一级函数处理并将输入数据保留在函数生成之外。然后你可以让你的功能脱离粘性并只调用一次。

它还可以推广到任何大小的输入数据帧,而不只是一行有5行。

#Build a dataframe
df <- data.frame(a=c(1, 2, 3, 4, 5), b=c(4, 3, 1, 2, 6))

#Build a function
fun <- approxfun(x = df$a, y = df$b, yright=max(df$b), method="linear", f = 1)

#Set up the plot range
x <- seq(1, 4.3, 0.01)

#Plot the function
plot(x, fun(x), type="l", xlim=c(0, max(df$a)), ylim=c(0, max(df$b)), lwd=2, col="red")

这可能不是你最终需要的,但它确实消除了一定程度的复杂性并提供了一个更清晰的起点。

答案 2 :(得分:1)

这在大型蒙特卡罗模拟中可能不会更好,但对于更简单的情况,将x和y范围作为创建函数的输出的属性而不是在具有创建函数的列表中包含可能更清楚。这样goo就像Davor提到的那样,是一个更直接的工厂。您还可以将函数的结果作为对象(此处使用S3),以便可以更简单地绘制它。

goo <- function(inp.df) {
  out.fun <- approxfun(x=inp.df$a, y=inp.df$b, yright=max(inp.df$b), 
                       method="linear", f=1)
  xmax <- inp.df$a[5]
  ymax <- inp.df$b[5]
  function(...) {
    structure(data.frame(x=x, y = out.fun(...)), 
              limits=list(x=xmax, y=ymax), 
              class=c("goo","data.frame"))
   }
}

plot.goo <- function(x, xlab="x", ylab="approx",  
                     xlim=c(0, attr(x, "limits")$x), 
                     ylim=c(0, attr(x, "limits")$y), 
                     lwd=2, col="red", ...) {
  plot(x$x, x$y, type="l", xlab=xlab, ylab=ylab, 
       xlim=xlim, ylim=ylim, lwd=lwd, col=col, ...)
}

然后为了制作数据框的功能,你可以这样做:

df <- data.frame(a=c(1, 2, 3, 4, 5), b=c(4, 3, 1, 2, 6))
goodf <- goo(df)

要在矢量上使用它,你可以:

x <- seq(1, 4.3, 0.01) 
goodfx <- goodf(x)
plot(goodfx)