Advanced R Exercise Solution (4)

Xinchen Pan · 2018/06/12

Environments

More unanswered questions.

Environment basics

  1. List three ways in which an environment differs from a list.
  • Every name in an environment is unique.
  • The names in an environment are not ordered (i.e., it doesn’t make sense to ask what the first element of an environment is).
  • An environment has a parent.
  • Environments have reference semantics.
  1. If you don’t supply an explicit environment, where do ls() and rm() look? Where does <- make bindings?

ls() will list all parents of the global environment. rm() will remove the elements from current environment.

<- makes bindings in current enviroment(.GlobalEnv)

  1. Using parent.env() and a loop (or a recursive function), verify that the ancestors of globalenv() include baseenv() and emptyenv(). Use the same basic idea to implement your own version of search().
env <- .GlobalEnv
while(!identical(env, emptyenv())){
  env <- parent.env(env)
  if(identical(env, baseenv()) | identical(env, emptyenv()))
    print(env)
}
## <environment: base>
## <environment: R_EmptyEnv>
search_new <- function(env = .GlobalEnv){
  results <- NULL
  while(!identical(env, emptyenv())){
    results <- c(environmentName(env), results)
    env <- parent.env(env)
  }
    results
}

Recursing over environments

  1. Modify where() to find all environments that contain a binding for name.

Not sure

where <- function(name, env = parent.frame()) {
  {
    stopifnot(is.character(name), length(name) == 1)
    env <- as.envi(env)
    if (identical(env, emptyenv())) {
        stop("Can't find ", name, call. = FALSE)
    }
    if (exists(name, env, inherits = FALSE)) {
        env
    }
    else {
        where(name, parent.env(env))
    }
}
}
  1. Write your own version of get() using a function written in the style of where().
get_new <- function(name, env = parent.frame()) {
  {
    stopifnot(is.character(name), length(name) == 1)
    env <- as.environment(env)
    if (identical(env, emptyenv())) {
        stop("Can't find ", name, call. = FALSE)
    }
    if (exists(name, env, inherits = FALSE)) {
        env[[name]]
    }
    else {
        get_new(name, parent.env(env))
    }
}
}
  1. Write a function called fget() that finds only function objects. It should have two arguments, name and env, and should obey the regular scoping rules for functions: if there’s an object with a matching name that’s not a function, look in the parent. For an added challenge, also add an inherits argument which controls whether the function recurses up the parents or only looks in one environment.
fget_new <- function(name, env = parent.frame()) {
  {
    stopifnot(is.character(name), length(name) == 1)
    env <- as.environment(env)
    if (identical(env, emptyenv())) {
        stop("Can't find ", name, call. = FALSE)
    }
    if (exists(name, env, inherits = FALSE) & is.function(env[[name]])) {
        env[[name]]
    }
    else {
        fget_new(name, parent.env(env))
    }
}
}
  1. Write your own version of exists(inherits = FALSE) (Hint: use ls().) Write a recursive version that behaves like exists(inherits = TRUE).
exists_new <- function(name, env = parent.frame()){
  stopifnot(is.character(name), length(name) == 1)
  env <- as.environment(env)
  name %in% ls(envir = env)
}

exists_inherit <- function(name, env = parent.frame()){
   if (identical(env, emptyenv())) {
        stop("Can't find ", name, call. = FALSE)
    }
    if ( name %in% ls(envir = env)) {
       TRUE
    }
    else {
       exists_inherit(name, parent.env(env))
    }
}

Function environments

  1. List the four environments associated with a function. What does each one do? Why is the distinction between enclosing and binding environments particularly important?

enclosing environment is used for lexical scoping and determines how the function finds values;

binding environment of a function are all the environments which have a binding to it. I determines how we find the function.

execution environment: Calling a function creates an ephemeral execution environment that stores variables created during execution.

calling environment: Every execution environment is associated with a calling environment, which tells you where the function was called.

The distinction between enclosing and binding environments is important because package namespaces keep packages independent

Binding names to values

  1. What does this function do? How does it differ from <<- and why might you prefer it?

This function looks for the variable name in the environment. If it exists, it will modifiy it to 10.

rebind <- function(name, value, env = parent.frame()) {
  if (identical(env, emptyenv())) {
    stop("Can't find ", name, call. = FALSE)
  } else if (exists(name, envir = env, inherits = FALSE)) {
    assign(name, value, envir = env)
  } else {
    rebind(name, value, parent.env(env))
  }
}
a <- 10
rebind("a", 10)
  1. Create a version of assign() that will only bind new names, never re-bind old names. Some programming languages only do this, and are known as single assignment languages.
rebind <- function(name, value, env = parent.frame()) {
  if (identical(env, emptyenv())) {
    stop("Can't find ", name, call. = FALSE)
  } else if (exists(name, envir = env, inherits = FALSE) &
      get(name) == value ) {
      stop(name, " already exists", call. = FALSE)
  } else if(exists(name, envir = env, inherits = FALSE)) {
      assign(name, value, envir = env)
  } else {
      rebind(name, value, parent.env(env))
  }
}
b <- 3
rebind("b", 1)
  1. Write an assignment function that can do active, delayed, and locked bindings. What might you call it? What arguments should it take? Can you guess which sort of assignment it should do based on the input?