0

I have run into a package that uses the substitute function to parse parameters in one of its functions. The function used is more or less as follows:

f <- function(...) {
    ref <- as.list(substitute(list(...))[-1])
    # for clarity
    print(ref)
    counter <- 0L
    for (i in ref) {
        if (as.character(i[1]) == "==") {
            counter <- counter + 1L
        }
    }
    return(counter)
}


f(df1$a == df1$b, df1$b == df1$c)
#> [[1]]
#> df1$a == df1$b
#> 
#> [[2]]
#> df1$b == df1$c
#> [1] 2

Created on 2019-10-24 by the reprex package (v0.3.0)

I would like to pass df1$a == df1$b, df1$b == df1$c as a character, but I cannot find a way to do it as I cannot override the substitute function. So far I have tried a few options but none of them seem to work:

char <- "df1$a == df1$b, df1$b == df1$c"

f(get(char))
#> [[1]]
#> get(char)
#> [1] 0
f(parse(text = char))
#> [[1]]
#> parse(text = char)
#> [1] 0
f(eval(parse(text = char)))
#> [[1]]
#> eval(parse(text = char))
#> [1] 0

Created on 2019-10-24 by the reprex package (v0.3.0)

EDIT:

After @Roland's answer solving my initial question, I'm wondering if there is also an easy way to solve a slightly more general case with a function using another parameter:

f2 <- function(parameter, ...) {
  ref <- as.list(substitute(list(...))[-1])
  # for clarity
  print(ref)
  counter <- 0L
  for (i in ref) {
    if (as.character(i[1]) == "==") {
      counter <- counter + 1L
    }
  }
  return(counter)
}
Jon Nagra
  • 1,538
  • 1
  • 16
  • 36
  • 1
    Just do `do.call(f2, c(list(parameter = somevalue), char))`. – Roland Oct 24 '19 at 09:22
  • @Roland thanks a lot for your prompt response again. I never used `substitute` and I rarely use `do.call`. It perfectly fits the case. – Jon Nagra Oct 24 '19 at 09:27

1 Answers1

2

You cannot break out of substitute. That's not possible because the function parameters are not evaluated. But you can do this:

char <- "df1$a == df1$b, df1$b == df1$c"

#turn char into list of language objects
char <- strsplit(char, ",", fixed = TRUE)[[1]]
char <- as.list(parse(text = char))

#pass to f
do.call(f, char)
#[[1]]
#df1$a == df1$b
#
#[[2]]
#df1$b == df1$c
#
#[1] 2
Roland
  • 127,288
  • 10
  • 191
  • 288
  • Thanks a lot it does the work for me. I will edit the question to put a slightly more generic way including another parameter in the function. I used a wrapper but I wonder if there is a better way. – Jon Nagra Oct 24 '19 at 09:16