4

I don't get, why glue_data doesn't use a variable available from parent parent environment? See code below. Function test_call_glue_directly returns desired result. Function test_call_glue returns error Error in eval(x, envir = data, enclos = envir) : object 'c_1' not found

Of course, my real function has more than one variable. formula_1 might refer to {c_2} and {c_3}. Or {c_456}, {c_7}, {c_8}. The count of c_* might be different.

Is there a way how to create new environment specifically for calling this glue_data? Or pull variable from parent environment? Or some better way how to organize my code? I don't want to pass the parameter c_1 to funciton fill_1 because in my real script there could be unknown count of variables with unknown names.

fill_1 <- function(letts, n0, formula_x) {
  purrr::cross_df(list(row = n0, column = letts)) %>%
    glue::glue_data(formula_x) %>%
    matrix(ncol = length(letts)) %>%
    as.data.frame
}


test_call_glue <- function() {
  c_1 <- "C"
  formula_1 <-  "={c_1}{row}"
  fill_1(c("A"), c(1:5), formula_1)
}


test_call_glue_directly <- function() {
  # The function without wrapping glue_data in function
  c_1 <- "C"
  formula_1 <-  "={c_1}{row}"
  purrr::cross_df(list(row = c(1:5), column = c("A"))) %>%
    glue::glue_data(formula_1) %>%
    matrix(ncol = 1) %>%
    as.data.frame
}

test_call_glue_directly()
test_call_glue()
apc
  • 131
  • 9

3 Answers3

2

I'm coming back to revisit my question. Since then I have learned to use dots as argument to my custom function:

fill_3a <- function(x, ...) {
  paste(paste0(x, c_1), paste0(x, c_2), sep=":::")
}
test_call_paste_a <- function() {
  c_1 <- "C"
  c_2 <- "H"
  ans <- fill_3a("4", c_1, c_2)
  return(ans)
}

test_call_paste_a()

Probably it doesn't make sense to call unnamed argument my name, but it makes sense for my use case, where the first argument is string used for glue::glue (variables are substituted to their values by name).

apc
  • 131
  • 9
1

It seems that this one works. The function fill_1 does not find c_1 as it is called from the .GlobalEnv. Your functions are created in your environment .GlobalEnv and that's where they look to find their variables. Here I'm putting c_1 back in .GlobalEnv using <<-.

test_call_glue <- function() {
  c_1 <- "C"
  c_1<<-c_1
  formula_1 <-  "={c_1}{row}"
  ff<-fill_1(letts=c("A"), n0=c(1:5), formula_x=formula_1)
  return(ff)
}

Note that this one works too, the function fill_1 environment is now test_call_glue(), so it has access to c_1

test_call_glue <- function() {
  fill_1 <- function(letts, n0, formula_x) {
      pp<-purrr::cross_df(list(row = n0, column = letts)) %>%
        glue::glue_data(formula_x) %>%
        matrix(ncol = length(letts)) %>%
        as.data.frame
      return(pp)    
  }
  c_1 <- "C"
  formula_1 <-  "={c_1}{row}"
  ff<-fill_1(letts=c("A"), n0=c(1:5), formula_x=formula_1)
  return(ff)
}

Another solution is to take c_1 from the parent environment of the function.

fill_1 <- function(letts, n0, formula_x) {
  c_1 <- get ("c_1", parent.frame())
  pp <- purrr::cross_df(list(row = n0, column = letts)) %>%
      glue::glue_data(formula_x) %>%
      matrix(ncol = length(letts)) %>%
      as.data.frame

  return(pp)    
}

Here I create an environment, where I store the variable and then retrieve it. I think that's what you want.

envir_glue<-new.env()

test_call_glue <- function() {
  c_1 <- "C"
  assign("c_1",c_1,envir=envir_glue)
  formula_1 <-  "={c_1}{row}"
  ff<-fill_1(letts=c("A"), n0=c(1:5), formula_x=formula_1)
  return(ff)
}

fill_1 <- function(letts, n0, formula_x) {
  c_1 <- get ("c_1", envir_glue)
  pp <- purrr::cross_df(list(row = n0, column = letts)) %>%
      glue::glue_data(formula_x) %>%
      matrix(ncol = length(letts)) %>%
      as.data.frame

  return(pp)    
}
Cedric
  • 2,412
  • 17
  • 31
  • This approach works. However formula_1 might refer to {c_2} and {c_3}. Or {c_456}, {c_7}, {c_8}. The count of c_* might be different. – apc Nov 01 '17 at 17:31
  • Your second code snippet did solve my problem, thanks! – apc Nov 02 '17 at 08:32
  • I would still really like to understand to which environment `glue_data` refers. Or how to pass list to function and allowuse variables from this list. – apc Nov 02 '17 at 08:34
  • 1
    `> environment(fill_1) > environment(test_call_glue) `. I'll try to add another edit to explain how to use environements. – Cedric Nov 02 '17 at 09:00
  • Note that I tried something like https://stackoverflow.com/questions/23890522/set-a-functions-environment-to-that-of-the-calling-environment-parent-frame-fr but then the eval is set to your parent environment, and you don't see the variables passed to the function. So I think the last solution is the best – Cedric Nov 02 '17 at 09:12
1

Thanks to Cedric! I finally understood what's wrong.

First, it has nothing to do with glue.Its the same with other functions. The optional parameter .env misled me.

My final solution:

fill_3 <- function(x, g) {
  attach(g)
  paste0(x, c_1)
}

test_call_paste <- function() {
  c_1 <- "C"
  c_2 <- "H"
  g_list <- list(c_1=c_1, c_2=c_2)
  ans <- fill_3("4", g_list)
  return(ans)
}

test_call_paste()

This way I can keep fill function as nice abstraction and take care of actual formula string and variables in wrapper function.

apc
  • 131
  • 9