Nova
Nova

Reputation: 618

Function not able to find a formula object that was defined in the local environment of that function

I am attempting to build a function which can accept a list of formula as an argument.

I begin by defining the variables that will be input as the arguments into the function. The first two variables are named vectors that contain boolean information.

named_variable_vector <- c(0, 1, 1)
names(named_variable_vector) <- c("M", "E", "L")
named_variable_vector

named_parameter_vector <- c(0, 0)
names(named_parameter_vector) <- c("G_e", "L_e")
named_parameter_vector

The third argument is a list of formula objects. Formula is a "language" class object that doesn't get evaluated until explicitly told to do so using the f_eval() function.

logic_list <- list(~((G_e + 1)*(L + L_e + L*L_e))%%2, ~M , ~((G_e + 1)*E*L_e)%%2)

Ok, time to build the function. First step is to create local variables within the function. These local variables will be named after the names of the named_vector arguments.

Boolean_state_space <- function(variables, parameters, logic) {

  variable_names <- names(variables)
  parameter_names <- names(parameters)

  ####generate local variables

  #assign variable values to variable names
  for (i in 1:length(variables)) {
    assign(variable_names[i], variables[i])

  }
  #assign parameter values to parameter names
  for (j in 1:length(parameters)) {
    assign(parameter_names[j], parameters[j])
  }

Then I initialize a matrix. After that, I print out the local variables to test if they were successfully made. This is just a test line and wouldn't be in the final version of the function.

#initialize matrix
  state_space_matrix <- matrix(nrow = 4, ncol = length(c(variable_names, parameter_names)))
  colnames(state_space_matrix) <- c(variable_names, parameter_names)
  rownames(state_space_matrix) <- c(1:nrow(state_space_matrix))
  row.names(state_space_matrix)[1] <- c("t")


  state_space_matrix[1,] <- c(variables, parameters)
  state_space_matrix[,c(ncol(state_space_matrix) - 1, ncol(state_space_matrix) )] <-parameters
  print(state_space_matrix) #test to see if matrix was initialized succesfully
  print(c(M, E, L, G_e, L_e)) #test to see if local variables were successfully defined within the function

Finally, time to use the logic argument! The goal here is to have the function use the equations provided in the logic argument to "update" the next row of the matrix by using the values available in the previous row.

This first line evaluates the equations in order, then assigns those values to the next row in the matrix. This is where the error occurs.

  for (k in 2:nrow(state_space_matrix)) {

    for (h in 1:length(variable_names)) {
      state_space_matrix[k, variable_names[h]] <- f_eval(logic[[h]])
      print("test")

    }

The second line now reassigns the values for the variables, using the values in the most recently updated row of the matrix.

for (g in 1:length(variable_names)) {
      assign(variable_names[g], state_space_matrix[k, g])
    } 
  }
  print(state_space_matrix)  
}  #end of function

Ok, when I ran the function with the arguments I had set in the beginning:

Boolean_state_space(named_variable_vector, named_parameter_vector, logic_list)

I get the following error:

Error in eval(expr, data, expr_env) : object 'G_e' not found 

I can't for the life of me figure out why. Why is the function not seeing the local variables I had defined in it earlier? Those variables need to be used to evaluate the formula.

P.S. I am sure there may be a better/more elegant way of writing this function using a similar argument format and I would love to know them. But for learning purposes, I really want to know if my method of writing this function can work.

P.P.S. My method works great when I run the internals of the function separately (AKA in the global environment). Results look like this: enter image description here

Upvotes: 0

Views: 138

Answers (1)

Daniel Winkler
Daniel Winkler

Reputation: 497

Solution with parameter list

I solved this by being explicit about the data using a parameter list instead of relying on the correct environments.

Boolean_state_space <- function(variables, parameters, logic) {

    variable_names <- names(variables)
    parameter_names <- names(parameters)

####generate local variables

                                        #assign variable values to variable names
    for (i in 1:length(variables)) {
        assign(variable_names[i], variables[i])

    }
    print(variable_names)
                                        #assign parameter values to parameter names
    for (j in 1:length(parameters)) {
        assign(parameter_names[j], parameters[j])
    }


                             #initialize matrix
    state_space_matrix <- matrix(nrow = 4, ncol = length(c(variable_names, parameter_names)))
    colnames(state_space_matrix) <- c(variable_names, parameter_names)
    rownames(state_space_matrix) <- c(1:nrow(state_space_matrix))
    row.names(state_space_matrix)[1] <- c("t")


    state_space_matrix[1,] <- c(variables, parameters)
    state_space_matrix[,c(ncol(state_space_matrix) - 1, ncol(state_space_matrix) )] <-parameters
    print(state_space_matrix) #test to see if matrix was initialized succesfully
    print(c(M, E, L, G_e, L_e)) #test to see if local variables were successfully defined within the function

## CREATE PARAMETER LIST

    param_list <- split(unname(parameters), names(parameters))
    param_list <- c(split(unname(variables), names(variables)), param_list)
    cols <- names(variables)
    for (k in 2:nrow(state_space_matrix)) {

        for (h in 1:length(variable_names)) {
            state_space_matrix[k, cols[h]] <- lazyeval::f_eval(logic[[h]], param_list)
            print("test")

        }
## UPDATE PARAMETER LIST WITH CURRENT VALUES 
        for (g in 1:length(variable_names)) {
            param_list[variable_names[g]] <- state_space_matrix[k, g]
        }
    }
    print(state_space_matrix)
}

Boolean_state_space(named_variable_vector, named_parameter_vector, logic_list)

Alternative solution using environments

Boolean_state_space <- function(variables, parameters, logic) {

    variable_names <- names(variables)
    parameter_names <- names(parameters)

####generate local variables

                                        #assign variable values to variable names
    for (i in 1:length(variables)) {
        assign(variable_names[i], variables[i])

    }
                                        #assign parameter values to parameter names
    for (j in 1:length(parameters)) {
        assign(parameter_names[j], parameters[j])
    }

    state_space_matrix <- matrix(nrow = 4, ncol = length(c(variable_names, parameter_names)))
    colnames(state_space_matrix) <- c(variable_names, parameter_names)
    rownames(state_space_matrix) <- c(1:nrow(state_space_matrix))
    row.names(state_space_matrix)[1] <- c("t")


    state_space_matrix[1,] <- c(variables, parameters)
    state_space_matrix[,c(ncol(state_space_matrix) - 1, ncol(state_space_matrix) )] <-parameters
    print(state_space_matrix) #test to see if matrix was initialized succesfully
    print(c(M, E, L, G_e, L_e)) #test to see if local variables were successfully defined within the function
    print(parent.frame())
# Get correct environment
    env <- pryr::where(variable_names[1])
    for (k in 2:nrow(state_space_matrix)) {

        for (h in 1:length(variable_names)) {
# Set environment to evaluate functions in 
            rlang::f_env(logic[[h]]) <- env
            state_space_matrix[k, variable_names[h]] <- lazyeval::f_eval(logic[[h]])
            print("test")

        }

        for (g in 1:length(variable_names)) {
            assign(variable_names[g], state_space_matrix[k, g])
        } 
    }
    print(state_space_matrix)  
}

Boolean_state_space(named_variable_vector, named_parameter_vector, logic_list)

Upvotes: 1

Related Questions