R中的高级函数-有正式的合成操作符或咖喱函数吗?

0md85ypi  于 2023-02-10  发布在  其他
关注(0)|答案(6)|浏览(137)

我可以在R中创建一个组合操作符:

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

像这样使用:

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

但是我想知道是否有一个官方的函数集来做这类事情和其他操作,比如在R中进行currying。主要是为了减少我代码中括号,函数关键字等的数量。
我的咖喱功能:

> 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做过了吗?

qc6wkl3g

qc6wkl3g1#

这两个函数实际上都存在于Peter Danenberg的the roxygen packagesee the source code here)中(最初基于Byron Ellis's solution on 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
92vpleto

92vpleto2#

现在,R中函数式编程的标准位置是functional库。
从库中:
函数:Curry、Compose和其他高阶函数
示例:

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

起重机:https://cran.r-project.org/web/packages/functional/index.html
PS:这个库替代了ROxigen库。

qnyhuwrf

qnyhuwrf3#

roxygen包中有一个名为咖喱的函数。
通过R Mail Archive上的this conversation找到。

pdsfdshx

pdsfdshx4#

如果您希望变量的“名称”准确地传递,则需要更复杂的方法。
例如,如果你使用plot(rnorm(1000),rnorm(1000)),那么你会在x轴和y轴上得到漂亮的标签。

> 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 <- 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的外部变量不会有问题。现在,所有的变量都在咖喱被调用的地方求值。

wgxvkvu9

wgxvkvu95#

在package purrr中,现在有一个函数partial

s8vozzvw

s8vozzvw6#

如果你已经在使用tidyverse的purrr包,那么purrr::partial是一个自然的选择,从purrr::partial的描述可以看出:

# Partial is designed to replace the use of anonymous functions for
# filling in function arguments. Instead of:
compact1 <- function(x) discard(x, is.null)

# we can write:
compact2 <- partial(discard, .p = is.null)

相关问题