29

Suppose I have a function in R that takes multiple arguments, and I'd like to reduce it to a function of fewer arguments by setting some of the arguments to pre-specified values. I'm trying to figure out what is the best way to do this is in R.

For example, suppose I have a function

f <- function(a,b,c,d){a+b+c+d}

I'd like to create or find a function partial that would do the following

partial <- function(f, ...){
#fill in code here
}
new_f <- partial(f, a=1, c= 2)

new_f would be a function of b and d and would return 1+b+2+d

In python I would do

from functools import partial

def f(a,b,c,d):
    return a+b+c+d

new_f = partial(f, a=1, c= 2)

I'm actually doing this repeatedly and so I need for this to be as efficient as possible. Can anyone point me to the most efficient way to do this? Right now the best I can do is

partial <- function(f, ...){
    z <- list(...)
    formals(f) [names(z)] <- z
    f
}

Can anyone let me know of a faster way or the best way to do this? This is simply too slow.

jopasserat
  • 5,721
  • 4
  • 31
  • 50
k13
  • 713
  • 8
  • 17
  • 2
    You could either change its formal arguments with `formals(f)[c("a", "c")] <- list(1, 3)` or use `pryr::partial(f, a = 1, c = 2)` – Rich Scriven Aug 24 '15 at 03:11
  • thanks. if you post as answer I'll accept. pryr's partial was what I wanted. – k13 Aug 24 '15 at 03:13

4 Answers4

25

You could roll your own without too much coding using do.call:

partial <- function(f, ...) {
  l <- list(...)
  function(...) {
    do.call(f, c(l, list(...)))
  }
}

Basically partial returns a function that stores f as well as the originally provided arguments (stored in list l). When this function is called, it is passed both the arguments in l and any additional arguments. Here it is in action:

f <- function(a, b, c, d) a+b+c+d
p <- partial(f, a=2, c=3)
p(b=0, d=1)
# [1] 6
josliber
  • 43,891
  • 12
  • 98
  • 133
  • For list as a simple parameter, use this code `partial_list<-function(f, L){ function(...){ do.call(f, c(L, list(...))) } } ` – JP Zhang Dec 02 '18 at 02:41
21

There are functions in the pryr package that can handle this, namely partial()

f <- function(a, b, c, d) a + b + c + d 
pryr::partial(f, a = 1, c = 2)
# function (...) 
# f(a = 1, c = 2, ...)

So you can use it like this -

new_fun <- pryr::partial(f, a = 1, c = 2)
new_fun(b = 2, d = 5)
# [1] 10
## or if you are daring ...
new_fun(2, 5)
# [1] 10

You could also simply change f()'s formal arguments with

f <- function(a, b, c, d) a + b + c + d 
formals(f)[c("a", "c")] <- list(1, 2)
f
# function (a = 1, b, c = 2, d) 
# a + b + c + d
f(b = 2, d = 5)
# [1] 10

But with the latter, you must name the b and d arguments in f() to avoid an error when you want to leave a and c as their default values.

Rich Scriven
  • 97,041
  • 11
  • 181
  • 245
  • 1
    Seems like there's also a `partial` inside the `purrr` package; not sure of the differences though. This helped get me to the right spot though, so +1! – dwanderson Jun 21 '17 at 17:56
10

You have also Curry from package functional:

library(functional)

f <- function(a, b, c, d) a+b+c+d
ff = Curry(f, a=2, c=10)

ff(1,5)
#[1] 18

ff(b=1,d=5)
#[1] 18
Rich Scriven
  • 97,041
  • 11
  • 181
  • 245
Colonel Beauvel
  • 30,423
  • 11
  • 47
  • 87
0

The purrr package from the tidyverse provides many functional programming tools, partial functions among them:

library(purrr)
f <- function(a,b,c,d){a+b+c+d}
new_f <- partial(f, a = 1, c = 2)

new_f(3,5)
#> [1] 11
shs
  • 3,683
  • 1
  • 6
  • 34