How to combine Curry() with Vectorize()?

2019-05-02 09:28发布

Consider the following function:

addAmount <- function(x, amount) {
  stopifnot(length(x) == 1)
  return(x + amount)
}

It can be used to add some amount to x:

> addAmount(x = 5, amount = 3)
[1] 8
> addAmount(x = 2, amount = 3)
[1] 5

However, x must be of length 1:

> addAmount(x = 7:9, amount = 3)
 Error: length(x) == 1 is not TRUE 

I added this restriction intentionally for exemplification.

Using Vectorize, it is possible to pass in a vector for x:

> Vectorize(addAmount)(x = 7:9, amount = 3)
[1] 10 11 12

So far, so good.

However, I'd like to turn my addAmount function into a "add 3" function, using currying:

add3 <- functional::Curry(addAmount, amount = 3)

This works as expected if x is of length 1 and fails (as expected) if x is not of length 1:

> add3(x = 5)
[1] 8
> add3(x = 7:9)
 Error: length(x) == 1 is not TRUE

The problem is: add3 cannot be vectorized:

> Vectorize(add3)(x = 7:9)
 Error: length(x) == 1 is not TRUE 

Somehow, the curried function is not "compatible" with Vectorize, i.e. it behaves as if it had not been vectorized at all.

Question: What can I do about this? How can currying and vectorization be combined? (And: What is going wrong?)


I found a workaround (heavily inspired by Hadley's add function) using environments instead of Curry, but I'm looking for a cleaner solution that doesn't require this kind of clumsy "factory" functions:

getAdder <- function(amount) {
  force(amount)
  addAmount <- function(x) {
    stopifnot(length(x) == 1)
    return(x + amount)
  }
  return(addAmount)
}

add3 <- getAdder(3)
Vectorize(add3)(x = 7:9)
[1] 10 11 12

Tested with R 3.4.1 and the functional package (version 0.6).

1条回答
贼婆χ
2楼-- · 2019-05-02 10:04

You can vectorize before currying:

add3 <- functional::Curry(Vectorize(addAmount), amount = 3)

add3(1:10)

[1] 4 5 6 7 8 9 10 11 12 13

查看更多
登录 后发表回答