38

我可以在 R 中创建一个 compose 运算符:

 `%c%` = function(x,y)function(...)x(y(...)) 

像这样使用:

 > numericNull = is.null %c% numeric
 > numericNull(myVec)
 [2] TRUE FALSE

但我想知道是否有一套官方的函数来做这种事情和其他操作,比如 R 中的柯里化。很大程度上这是为了减少我代码中括号、函数关键字等的数量。

我的咖喱功能:

> curry=function(...){
    z1=z0=substitute(...);z1[1]=call("list");
    function(...){do.call(as.character(z0[[1]]),
                          as.list(c(eval(z1),list(...))))}}
> p = curry(paste(collapse=""))
> p(letters[1:10])
[1] "abcdefghij"

这对于例如聚合特别好:

> df = data.frame(l=sample(1:3,10,rep=TRUE), t=letters[1:10])
> aggregate(df$t,df["l"],curry(paste(collapse="")) %c% toupper)
  l    x
1 1  ADG
2 2  BCH
3 3 EFIJ

我发现它比以下更优雅和可编辑:

> aggregate(df$t, df["l"], function(x)paste(collapse="",toupper(x)))
  l    x
1 1  ADG
2 2  BCH
3 3 EFIJ

基本上我想知道 - 这已经为 R 完成了吗?

4

4 回答 4

28

这两个函数实际上都存在于Peter Danenbergroxygen中(参见这里的源代码)(最初基于Byron Ellis 在 R-Help 上的解决方案):

Curry <- function(FUN,...) {
  .orig = list(...);
  function(...) do.call(FUN,c(.orig,list(...)))
}

Compose <- function(...) {
  fs <- list(...)
  function(...) Reduce(function(x, f) f(x),
                       fs,
                       ...)
}

请注意函数的用法Reduce,这在尝试在 R 中进行函数式编程时非常有用。请参阅 ?Reduce 了解更多详细信息(其中还包括其他函数,例如MapFilter)。

还有你的咖喱例子(在这种用法上略有不同):

> library(roxygen)
> p <- Curry(paste, collapse="")
> p(letters[1:10])
[1] "abcdefghij"

下面是一个例子来展示Compose(对字母应用三个不同的函数)的效用:

> Compose(function(x) x[length(x):1], Curry(paste, collapse=""), toupper)(letters)
[1] "ZYXWVUTSRQPONMLKJIHGFEDCBA"

您的最后一个示例将像这样工作:

> aggregate(df[,"t"], df["l"], Compose(Curry(paste, collapse=""), toupper))
  l    x
1 1  ABG
2 2 DEFH
3 3  CIJ

最后,这是一种做同样事情的方法plyr(也可以很容易地用byaggregate已经显示):

> library(plyr)
> ddply(df, .(l), function(df) paste(toupper(df[,"t"]), collapse=""))
  l   V1
1 1  ABG
2 2 DEFH
3 3  CIJ
于 2010-02-09T14:16:50.433 回答
7

R 中函数式编程的标准位置现在是functional库。

从图书馆:

函数式:Curry、Compose 和其他高阶函数

例子:

   library(functional)
   newfunc <- Curry(oldfunc,x=5)

克兰: https ://cran.r-project.org/web/packages/functional/index.html

PS:这个库替代了ROxigen库。

于 2015-09-30T12:13:25.253 回答
2

在roxygen包中有一个叫做 Curry 的函数。通过R Mail Archive 上的此对话
找到。

于 2010-02-09T14:15:58.910 回答
2

如果您希望变量的“名称”准确传递,则需要更复杂的方法。

例如,如果你这样做了,plot(rnorm(1000),rnorm(1000))那么你会在 x 轴和 y 轴上得到很好的标签。另一个例子是data.frame

> data.frame( rnorm(5), rnorm(5), first=rpois(5,1), second=rbinom(5,1,0.5) )
    rnorm.5. rnorm.5..1 first second
1  0.1964190 -0.2949770     0      0
2  0.4750665  0.8849750     1      0
3 -0.7829424  0.4174636     2      0
4  1.6551403  1.3547863     0      1
5  1.4044107 -0.4216046     0      0

并不是说 data.frame 为列分配了有用的名称。

Curry 的某些实现可能无法正确执行此操作,导致列名和绘图标签不可读。相反,我现在使用这样的东西:

Curry <- function(FUN, ...) {
    .orig = match.call()
    .orig[[1]] <- NULL # Remove first item, which matches Curry
    .orig[[1]] <- NULL # Remove another item, which matches FUN
    function(...) {
        .inner = match.call()
        .inner[[1]] <- NULL # Remove first item, which matches Curry
        do.call(FUN, c(.orig, .inner), envir=parent.frame())
    }
}

这很复杂,但我认为这是正确的。match.call将捕获所有参数,完全记住定义参数的表达式(这对于漂亮的标签是必要的)。问题是它捕获了太多的参数——不仅是,...而且是FUN. 它还记得被调用的函数的名称 ( Curry)。

因此,我们要删除前两个条目,.orig以便.orig真正对应于...参数。这就是我们做.orig[[1]]<-NULL两次的原因——每次都删除一个条目并将其他所有内容都向左移动。

这完成了定义,我们现在可以执行以下操作以获得与上面完全相同的内容

Curry(data.frame, rnorm(5), rnorm(5) )( first=rpois(5,1) , second=rbinom(5,1,0.5) )

关于envir=parent.frame(). 我用它来确保如果您有名为“.inner”或“.orig”的外部变量不会有问题。现在,所有变量都在调用 curry 的地方进行评估。

于 2014-09-30T20:27:54.007 回答