функции более высокого уровня в R - есть официальный оператор compose или функция карри - PullRequest
37 голосов
/ 09 февраля 2010

Я могу создать оператор составления в R:

 `%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 ]

28 голосов
/ 09 февраля 2010

Обе эти функции фактически существуют в пакете roxygen ( см. Исходный код здесь ) от Питера Даненберга (изначально был основан на решении Байрона Эллиса на 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. См. Сокращение для более подробной информации (которая также охватывает другие функции, такие как Map и Filter).

И ваш пример карри (немного отличается в этом использовании):

> 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 (также легко можно сделать с by или aggregate, как уже показано):

> library(plyr)
> ddply(df, .(l), function(df) paste(toupper(df[,"t"]), collapse=""))
  l   V1
1 1  ABG
2 2 DEFH
3 3  CIJ
6 голосов
/ 30 сентября 2015

Стандартным местом для функционального программирования на R теперь является библиотека functional.

Из библиотеки:

функционал: Curry, Compose и другие функции высшего порядка

Пример:

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

CRAN: https://cran.r -project.org / веб / пакеты / функционал / index.html

PS: эта библиотека заменяет библиотеку ROxigen.

2 голосов
/ 01 октября 2014

Требуется более сложный подход, если вы хотите, чтобы «имена» переменных проходили точно.

Например, если вы сделаете 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. Теперь все переменные оцениваются в том месте, где вызывается карри.

2 голосов
/ 09 февраля 2010

В пакете roxygen есть функция Curry.
Нашел через этот разговор в почтовом архиве R.

...