English 中文(简体)
R中的函数组合(以及高级函数)
原标题:Function Composition in R (and high level functions)
  • 时间:2011-02-07 07:08:01
  •  标签:
  • r
  • haskell

R中有类似函数合成的东西吗?

我认为在haskell中它有点像“(.)”,而在agda中它是环运算符。

Also, I find litte information on high level functional programming in R. I found the Functions "Reduce", "Map", "Filter"..., are there more? Any pointers?

最佳回答

您可以使合成功能如下:

composite<-function(f,g) function(...) f(g(...))

f<-function(x) x+1;
g<-function(x) x*2;
composite(f,g)(7)
composite(g,f)(7)

或者使其成为操作者。

关于第二点,有很多这样的;我认为最常用的是*apply系列(sapply、mapply、tapply、lapply、apply…)。

问题回答

函数包具有Compose函数,该函数可概括为任意数量的函数:

set.seed(123)
x <- matrix(runif(100), 10, 10)
mean(rowSums(scale(x)))
# [1] 5.486063e-18

library(functional)
Compose(scale, rowSums, mean)(x)
# [1] 5.486063e-18

(请注意,这些功能是从左到右应用的。)

现在purrr库中有一个compose函数。默认情况下,合成是从右到左进行的,如Haskell中所示,但可以使用.dir参数反转:

library(purrr)
f = function(x) x+1
g = function(x) x*3

> compose(g,f)(1)
[1] 6
> compose(f,g, .dir="forward")(1)
[1] 6

我想回答第一个问题。可以定义R组合运算符%.%那个

  • allows partial argument assignment to the component functions;
  • ensure environment sensitive functions like ls act normally.
  • the composition body show the actual component function names.

该运算符的灵感来自R4.1<code>pipeOp</code>运算符<code>|>。以下是预期结果:

## example component functions
ff <- function(n, a=1.0) 1:n*a
gg <- function(x, b=0.5) x^b
hh <- function(x, c=2.0, d=0.5) d * log(x, c)

## composition #1, rotate `c` in hh()
(tmp <- ff(a=1) %.% gg(b=2) %.% hh(x=3, d=1) %.% round(digits=3))
#  function (...)
#  round(hh(gg(ff(..., a = 1), b = 2), x = 3, d = 1), digits = 3)
tmp(9)
#  [1]   Inf 0.792 0.500 0.396 0.341 0.307 0.282 0.264 0.250

## composition #3: a component function is environment sensitive.
(ls() %.% grep(pattern="^[a-z]", value=TRUE) %.% toupper)(environment())
#  [1] "FF"  "GG"  "HH"  "TMP"

这是%.%的定义

"%.%" <- function(f, g, .dp=0)
{
    f <- substitute(f)
    g <- substitute(g)

    ## treatment of f()
    if("%.%" == all.names(f)[1])
    {
        ## f() is a %.% expression, expand it by a deeper call of %.%.
        f <- eval(as.call(append(as.list(f), c(.dp=.dp+1))))
    }
    else
    {
        ## f() is the deepest function, (i.e., the left most syntax of
        ## a %.% chain), insert ... as its first argument.
        f <- as.call(append(as.list(f), quote(...), 1L))
    }

    ## expands g() to g(f(...)) by treating f(...) as the 1st argument
    g <- as.call(append(as.list(g), f, 1L))

    ## pack up and return
    if(.dp > 0)
    {
        ret <- g # a deeper call of %.% return expanded expression
    }
    else
    {
        ## the top call of %.% build a function enclosing g(f(...))
        ret <- function(...) {}
        body(ret) <- g
        ## treat the function as if it was defined from outside.
        environment(ret) <- parent.frame()
    }
    ret
}

## example component functions
ff <- function(n, a=1.0) 1:n*a
gg <- function(x, b=0.5) x^b
hh <- function(x, c=2.0, d=0.5) d * log(x, c)

## composition #1, rotate `c` in hh()
(tmp <- ff(a=1) %.% gg(b=2) %.% hh(x=3, d=1) %.% round(digits=3))
#  function (...)
#  round(hh(gg(ff(..., a = 1), b = 2), x = 3, d = 1), digits = 3)
tmp(9)
#  [1]   Inf 0.792 0.500 0.396 0.341 0.307 0.282 0.264 0.250

## composition #2: rotate `x` in hh(); compose an anonymous function.
sapply(1:4, ff(a=1) %.% gg(b=2) %.% hh(d=1, c=2) %.% round(digits=3))
#  [[1]]
#  [1] 0
# 
#  [[2]]
#  [1] 0 2
# 
#  [[3]]
#  [1] 0.00 2.00 3.17
# 
#  [[4]]
#  [1] 0.00 2.00 3.17 4.00

## composition #3: a component function is environment sensitive.
(ls() %.% grep(pattern="^[a-z]", value=TRUE) %.% toupper)(environment())
#  [1] "FF"  "GG"  "HH"  "TMP"
## does not show `f`, `g` and `ret` (and `.dp`) as if the composition
## is outside of `%.%` s environment.

## composition #4: example from Answer #1 by [flodel]
set.seed(123)
x <- matrix(runif(100), 10, 10)
mean(rowSums(scale(x)))
# [1] 5.486063e-18
(scale %.% rowSums %.% mean)(x)
# [1] 5.486063e-18

目前,我无法获得空参数列表来处理函数体中缺少语法的允许空白参数的函数,因此我必须将environment()作为参数传递给lsgrep的组合(示例#3),尽管ls允许空参数。

值得一提的是,这里有一个以R(>;=4.1.0)为基的线性函数,使用Reduce来组成任意数量的函数,这里的scalerowSumsmean

mean(rowSums(scale(mtcars)))      # nested function call

f <- Reduce((g, h) {(x) h(g(x))}, list(scale, rowSums, mean))
f(mtcars)     # now, calling f gives same result as nested call above

这或多或少就是functional::Compose所做的。

注意事项

  • is shorthand for function as of R 4.1.0
  • The solutions that already have been provided are IMO preferable. For example, purrr::compose is more readable and flexible and does much more under the hood.




相关问题
Euler Problem in Haskell -- Can Someone Spot My Error

I m trying my hand at Euler Problem 4 in Haskell. It asks for that largest palindrome formed by multiplying two three-digit numbers. The problem was simple enough, and I thought my Haskell-fu was up ...

How does foldr work?

Can anybody explain how does foldr work? Take these examples: Prelude> foldr (-) 54 [10, 11] 53 Prelude> foldr (x y -> (x+y)/2) 54 [12, 4, 10, 6] 12.0 I am confused about these executions....

Efficient queue in Haskell

How can I efficiently implement a list data structure where I can have 2 views to the head and end of the list, that always point to a head a tail of a list without expensive calls to reverse. i.e: ...

Problem detecting cyclic numbers in Haskell

I am doing problem 61 at project Euler and came up with the following code (to test the case they give): p3 n = n*(n+1) `div` 2 p4 n = n*n p5 n = n*(3*n -1) `div` 2 p6 n = n*(2*n -1) p7 n = n*(5*n -3)...

Ways to get the middle of a list in Haskell?

I ve just started learning about Functional Programming, using Haskel. I m slowly getting through Erik Meijer s lectures on Channel 9 (I ve watched the first 4 so far) and in the 4th video Erik ...

haskell grouping problem

group :: Ord a => [(a, [b])] -> [(a, [b])] I want to look up all pairs that have the same fst, and merge them, by appending all the list of bs together where they have the same a and discarding ...

Closest equivalent to subprocess.communicate in Haskell

I want to do a popen() / python s subprocess.communicate from Haskell - start a program, give it stdin, and get its stdout/stderr. What s the most direct / Haskellish way to do this?