Base R has a function get()
that searches for a given name over the environment stack and returns its value after finding it. For example, we can use it like this.
x = 9
get ( "x" )
## [1] 9
get ( "mean" ) # inherits is set to TRUE by default
## function (x, ...)
## UseMethod("mean")
## <bytecode: 0x7fa52b0c08a0>
## <environment: namespace:base>
get ( "mean" , inherits = FALSE )
## Error in get("mean", inherits = FALSE): object 'mean' not found
mean = function () "fake mean"
get ( "mean" )
## function() "fake mean"
If you don’t understand why they returned the values they did, you can learn how
environment works by reading this post .
We can write our own version of get() using recursion. First we write a helper function that works the same as get(name, inherits = T)
.
get_helper = function ( name , env = parent.frame ()) {
# Returns the value that name binds to. Looks for name in the given
# environment and all its parents.
# name: string, name of an object
# env : environment object where the search begins. Default value is
# the global environment
if ( identical ( env , emptyenv ())) { # base case
stop ( "object '" , name , "'" , " not found" , call. = F )
} else if ( exists ( name , envir = env , inherits = F )) { # success case
env [[ name ]]
} else { # recursive case
get_helper ( name , parent.env ( env ))
}
}
get_helper ( "x" )
## [1] 9
get ( "mean" )
## function() "fake mean"
Next we can easily extend it to a more general version that takes an additional parameter inherits
.
get_gmlang = function ( name , env = parent.frame (), inherits = T ) {
# Returns the value that name binds to. If inherits = T, looks for name
# in the given environment and all its parents. Otherwise, looks
# for name only in the given environment.
#
# name: string, name of an object
# env : environment object where the search begins. Default value is
# the global environment
# inherits: logical
if ( inherits ) {
get_helper ( name , env )
} else if ( exists ( name , envir = env , inherits = F )) {
env [[ name ]]
} else {
stop ( "object '" , name , "'" , " not found" , call. = F )
}
}
e = new.env ()
e $ z = 100
get_gmlang ( "x" , env = e , inherits = T )
## [1] 9
get_gmlang ( "x" , env = e , inherits = F )
## Error: object 'x' not found
get_gmlang ( "z" , env = e )
## [1] 100
get_gmlang ( "z" ) # note: global environment is the parent of e
## Error: object 'z' not found
Moreover, we can easily extend get_helper()
to a function fget_helper()
that finds only function objects.
fget_helper = function ( name , env = parent.frame ()) {
# Returns the value that name binds to when name binds to a function.
# Looks for name in the given environment and all its parents.
#
# name: string, name of an object
# env : environment object where the search begins. Default value is
# the global environment
if ( identical ( env , emptyenv ())) { # base case
stop ( "Can't find " , name , " as a function object" , call. = F )
} else if ( class ( env [[ name ]]) == "function" ) { # success case
# if name not in env, env[[name]] returns NULL with class "NULL"
env [[ name ]]
} else { # fail case
fget_helper ( name , parent.env ( env ))
}
}
fget_helper ( "x" )
## Error: Can't find x as a function object
fget_helper ( "mean" )
## function() "fake mean"
rm ( "mean" )
fget_helper ( "mean" )
## function (x, ...)
## UseMethod("mean")
## <bytecode: 0x7fa52b0c08a0>
## <environment: namespace:base>