1

I have two functions, each of which return two values (as a list) and which operate over a limited domain:

# for x >= 0
f <- function(x) { return(list(a=x,b=2*x)) }
# for x < 0
g <- function(x) { return(list(a=x/2,b=x/4)) }

I want to do something like this:

fg <- function(x) { return(ifelse(x < 0, g(x), f(x))) }

And I'd like to get this:

> fg(c(1,2))
a$
[1] 1 2
b$
[2] 2 4

> fg(c(-1,-2)
a$
[1] -0.5 -1.0
b$
[2] -0.25 -0.50

Instead, I am getting this:

> fg(c(1,2))
[[1]]
[1] 1 2

[[2]]
[1] 2 4

> fg(c(-1,-2))
[[1]]
[1] -0.5 -1.0

[[2]]
[1] -0.25 -0.50

The "$a" and "$b" labels are getting lost.

With a single input, it's even worse:

> fg(1)
[[1]]
[1] 1

What am I doing wrong?

How can I achieve my objective?

David H
  • 1,461
  • 2
  • 17
  • 37
  • 1
    Not sure why you are getting `0`. I'm getting `1` with R 3.2.2. Anyway, try `x=1; ifelse(x > 0, 1:10, 1:5)`. As you can see, `ifelse` only returns the first element. At this point you should just use a regular `if-loop`: `fg <- function(x) if ( x < 0) g(x) else f(x)` – slamballais Feb 03 '16 at 01:18
  • 1
    If `x` can have multiple elements, you could try: `fg <- function(x) sapply(x, function(y) if (y < 0) g(y) else f(y), simplify=FALSE)`. – josliber Feb 03 '16 at 01:19
  • Related: http://stackoverflow.com/questions/31630642/print-function-in-ifelse –  Feb 03 '16 at 01:20
  • ifelse returns a value with the same shape as test..... – Gopala Feb 03 '16 at 01:21
  • @Laterow -- really sorry, that was a typo. I fixed it. Still the question persists. – David H Feb 03 '16 at 01:30
  • @josilber -- yes, intention is for x to be a vector. I was wondering if this could be done in a vectorized fashion (without sapply). – David H Feb 03 '16 at 01:35
  • What is wrong with the traditional `if` and `else` solution posted in the first comment? – Gopala Feb 03 '16 at 01:38
  • @DavidH In your code, `f(x)` and `g(x)` do not actually depend on `x`. Do your actual functions have a dependence? If so, what sort? – josliber Feb 03 '16 at 01:38
  • @Gopala `x` is a vector and the OP wants a vectorized operation. – josliber Feb 03 '16 at 01:38
  • 2
    I think it is better to revise the question with proper sample input and expected output. – Gopala Feb 03 '16 at 01:40

1 Answers1

1

I have modified your code,

# for x >= 0
f <- function(x) { return(list(a=x,b=2*x)) }
# for x < 0
g <- function(x) { return(list(a=x/2,b=x/4)) }
fg <- function(x) {
    tmp <- lapply(x, function(y) switch(as.character(y > 0), 'TRUE' = f(y), 'FALSE' = g(y)))
    a <- sapply(tmp, function(y) y$a)
    b <- sapply(tmp, function(y) y$b)
    out <- list(a = a, b = b)
    return(out)
}

This is another version, which gives the results you desire but does not need the functions f and g. Also, the computation is vectorised:

fg2 <- function(x) {
    list(
        a = x * (1/2)^(x < 0), 
        b = x * 2 * (1/8)^(x < 0)
    )
}

Below are some examples,

> fg2(1)
$a
[1] 1

$b
[1] 2

> fg2(1:2)
$a
[1] 1 2

$b
[1] 2 4

> fg2(-2:2)
$a
[1] -1.0 -0.5  0.0  1.0  2.0

$b
[1] -0.50 -0.25  0.00  2.00  4.00
pe-perry
  • 2,591
  • 2
  • 22
  • 33
  • Seems like more of a "workaround". The lapply call means that the f and g functions are being called one at a time, rather than being passed vectors -- no? I was hoping for a vectorized solution – David H Feb 03 '16 at 14:46
  • @DavidH I have edited my answer, does it meet your requirement? – pe-perry Feb 04 '16 at 03:02