7

I am trying to create a function where users can select the operator they want to use, which result in a different output. But I can't seem to get it to work. I know that we can't assign operators into an R object and then use it as an operator based on the R object name. Is there a way I could do this? Or perhaps a better way to write the function?

test <- function(items, operator = "+"){
bank_alpha <- matrix(ncol=6)
colnames(bank_alpha) <- colnames(bank_alpha, do.NULL = FALSE, prefix = "Q")
colnames(bank_alpha)[6] <- "A"
alphabet <- LETTERS[seq(1:26)]

 for (i in 1:items) {
  item <- c(alphabet[i], alphabet[i operator 1], alphabet[i operator  2], alphabet[i operator  3], alphabet[i operator  4], alphabet[i operator 5])
  bank_alpha <- rbind(bank_alpha, item)
  bank_alpha <- na.omit(bank_alpha)
}
return(bank_alpha)
}

  test(items=4, operator = "-") 
Sam
  • 261
  • 2
  • 12
  • 1
    You want to use the "operator" argument as a function inside `test`? I.e. `operator = "+"; match.fun(operator)(1, 3)`? There are plenty examples of higher level R functions that accept functions as inputs (e.g. `Reduce`, `lapply`, `outer` etc) – alexis_laz Sep 04 '16 at 10:56
  • Yes, I see now. Thank you for the quick response! – Sam Sep 04 '16 at 11:37

3 Answers3

5

You can use the get() functionally to dynamically call the operator functions, e.g.:

> get('+')(5, 2)
[1] 7

The get() function takes a string as input and returns a reference to the function corresponding to that string, if it exists.

Usually operators are called with one function argument on the left and one on the right (e.g. 5 + 2).

The + operator function itself however just takes two arguments as input:

> args('+')
function (e1, e2) 
NULL

This is why we can call it as we did above.

Keith Hughitt
  • 4,860
  • 5
  • 49
  • 54
4

Check out do.call, which takes the name of a function as an argument. With

operator <- "+"
do.call(operator, list(2,3)

you will get 5 as the result.

In your example:

test <- function(items, operator = "+"){
  bank_alpha <- matrix(ncol=6)
  colnames(bank_alpha) <- colnames(bank_alpha, do.NULL = FALSE, prefix = "Q")
  colnames(bank_alpha)[6] <- "A"
  alphabet <- LETTERS[seq(1:26)]

  for (i in 1:items) {
    item <- c(alphabet[i], alphabet[do.call(operator, list(i,1))], alphabet[do.call(operator, list(i,2))], alphabet[do.call(operator, list(i,3))], alphabet[do.call(operator, list(i,4))], alphabet[do.call(operator, list(i,5))])
    bank_alpha <- rbind(bank_alpha, item)
    bank_alpha <- na.omit(bank_alpha)
  }
  return(bank_alpha)
}

test(items=4, operator = "*") 

Beware, "-" doesn't make sense in this case.

shosaco
  • 5,915
  • 1
  • 30
  • 48
  • Yes, this is exactly what I needed. You are right regarding the "-". Thank you! – Sam Sep 04 '16 at 11:37
  • If it solved your question, don't hesitate to accept it as answer :) – shosaco Sep 04 '16 at 11:40
  • 1
    Note there will be a large number of function lookups if you use "do.call" like this, which will lead to a large performance overhead. I would recommend passing a function to "test" instead, possibly using the infix notation (see answer below) if that seems to lead to more readable code. – Tomas Kalibera Sep 05 '16 at 07:40
1

You can define your own operators in R using the % sign, which would allow you to use the infix notation in your code. See a related question for more details.

test <- function(items, `%op%` = `+`){
    bank_alpha <- matrix(ncol=6)
    colnames(bank_alpha) <- colnames(bank_alpha, do.NULL = FALSE, prefix = "Q")
    colnames(bank_alpha)[6] <- "A"
    alphabet <- LETTERS[seq(1:26)]

    for (i in 1:items) {
       item <- c(alphabet[i], alphabet[i %op% 1], alphabet[i %op% 2], alphabet[i %op% 3], alphabet[i %op% 4], alphabet[i %op% 5])
       bank_alpha <- rbind(bank_alpha, item)
       bank_alpha <- na.omit(bank_alpha)
    }
    return(bank_alpha)
}

test(items=4, `%op%` = `*`)
Community
  • 1
  • 1
Tomas Kalibera
  • 1,061
  • 9
  • 13
  • I always wanted to create a function that uses the %function%. Now I can. This is a great example. Thank you so much! – Sam Sep 05 '16 at 14:43