foo <- function() {
# how to know what environment_of_caller is
}
caller <- function() {
# environment_of_caller
foo()
}
A function that I'm writing needs to the know the environment of its caller. Can that be done without passing the environment in as an argument?
Assuming that you really need to do this, the function parent.frame() gives it.
foo <- function() {
parent.frame()$a
}
caller <- function() {
a <- 1
foo()
}
caller()
## [1] 1
however, normally one would write it like this (only foo is changed) as it gives the desired functionality but also the flexibility to change the environment used.
foo <- function(envir = parent.frame()) {
envir$a
}
caller <- function() {
a <- 1
foo()
}
caller()
## [1] 1
Related
I have created two functions below that are both defined in the global environment. You'll notice that foo() is called within bar() and they share the one input which is x. :
bar <- function() {
x <- 2
foo()
}
foo <- function(x) {
x * 1000
}
x is not explicitly defined when foo() is called within bar(), which is causing an error with the following code:
bar()
My question is this: is there a way to define the environment where foo() tries to find x? From my research, foo() is looking for x in the global environment, but ideally it should be taking x from the environment of bar(), where x <- 2.
I know that the following would fix the problem, but this isn't a reasonable option in the code I'm developing. The key is getting foo() to reference the x value from the environment of bar():
bar <- function() {
x <- 2
foo(x = x)
}
foo <- function(x) {
x * 1000
}
bar()
It seems like you maybe shoudl be using expressions here for functions like f. Maybe
foo <- quote(x * 1000)
bar <- function() {
x <- 2
eval(foo)
}
bar()
#[1] 2000
Or if it does have to be a function
foo <- function() {
x * 1000
}
bar <- function() {
x <- 2
environment(foo)<-environment()
foo()
}
bar()
#[1] 2000
Note that this doesn't permanently change foo, it just makes a local copy of foo in bar with a different enclosing environment.
The latter solution is much like my solution for the previous question: R specify function environment
This will give you something akin to what you want. The trick is to use a closure to define the function(/environment) you want, and then use the double left arrow assign to assign the value of x within bar. Double left arrow assign gives you the ability to write to a variable in the parent environment.
make_foo_bar_pair = function() {
# Define the shared state...
x <- 0
# Make your bar function
bar <- function() {
x <<- 2 # Use the double left arrow assign to modify the shared state
foo()
}
# Make your foo function
foo <- function() {
x * 1000 # Which references the shared state
}
# Return your linked functions...
list(foo, bar)
}
# Make a pair of functions with shared state...
pair = make_foo_bar_pair()
foo = pair[[1]]
bar = pair[[2]]
# Call them
foo() # 0 (foo uses the initial value of x that it closed over)
bar() # 2000
foo() # 2000
Each foo/bar pair that you create will reference a different x, so calling make_foo_bar_pair repeatedly will give you fresh foo/bar functions sharing a distinct x.
If you really need to use the global environment you could do this...
# Make your bar function
bar <- function() {
x <<- 2 # Use the double left arrow assign to modify the shared state
foo()
}
# Make your foo function
foo <- function() {
x * 1000 # Which references the shared state
}
...but doing things in the global environment like that feels like a code smell.
Let's assume I have the following code:
maybeBrowser <- function (msg) {
if (interactive()) {
cat(msg, "\n")
???
} else {
stop(msg)
}
}
foo <- function (cond, ...) {
if (cond) maybeBrowser("What a mess")
}
What would ??? have to look like in order to invoke browser() in the context of foo if cond evaluates to TRUE?
Perhaps not the most elegant, but this seems to do what I think you're asking for.
First, two notes:
I'm adding browser(); 1, knowing it doesn't work right away. The ;1 is because browser() will exit immediately if there is not some code after it. If there's something after the if/else block then you might not need it, but it's there for this. (This is only necessary with emacs/ESS: https://github.com/emacs-ess/ESS/issues/178)
I added a variable within the foo environment to demonstrate that we don't (then do) see it.
First, the failing attempt:
maybeBrowser <- function (msg) {
if (interactive()) {
cat(msg, "\n")
browser()
q
} else {
stop(msg)
}
}
foo <- function (cond, ...) {
cat(capture.output(environment()), "\n")
in_foo <- 1
if (cond) maybeBrowser("What a mess")
}
foo(TRUE)
# <environment: 0x000000001b2beba0>
# What a mess
# Called from: maybeBrowser("What a mess")
# Browse[1]>
debug at #5: q
# Browse[2]>
environment()
# <environment: 0x000000001b280030> <---- this is different
# Browse[2]>
ls()
# [1] "msg"
Now a tweak to the code, motivated by https://stackoverflow.com/a/23891089/3358272
maybeBrowser <- function (msg) {
if (interactive()) {
cat(msg, "\n")
return(evalq(browser(skipCalls=1), envir=parent.frame()))
} else {
stop(msg)
}
}
foo <- function (cond, ...) {
cat(capture.output(environment()), "\n")
in_foo <- 1
if (cond) maybeBrowser("What a mess")
}
foo(TRUE)
# <environment: 0x000000001b0b9d40>
# What a mess
# Called from: eval(quote({
# browser()
# 1
# ...
# Browse[1]>
debug at #4: [1] 1
# Browse[3]>
environment()
# <environment: 0x000000001b0b9d40> <---- this is now the same
# Browse[3]>
ls()
# [1] "cond" "in_foo"
However, this is not allowing you to continue, stepping through any following code in foo, so it is an incomplete answer. I think unfortunately that that may not be feasible ... but perhaps a more internals-cognizant R bubba will have more clarity on this.
I'm having a little trouble understanding why, in R, the two functions below, functionGen1 and functionGen2 behave differently. Both functions attempt to return another function which simply prints the number passed as an argument to the function generator.
In the first instance the generated functions fail as a is no longer present in the global environment, but I don't understand why it needs to be. I would've thought it was passed as an argument, and is replaced with aNumber in the namespace of the generator function, and the printing function.
My question is: Why do the functions in the list list.of.functions1 no longer work when a is not defined in the global environment? (And why does this work for the case of list.of.functions2 and even list.of.functions1b)?
functionGen1 <- function(aNumber) {
printNumber <- function() {
print(aNumber)
}
return(printNumber)
}
functionGen2 <- function(aNumber) {
thisNumber <- aNumber
printNumber <- function() {
print(thisNumber)
}
return(printNumber)
}
list.of.functions1 <- list.of.functions2 <- list()
for (a in 1:2) {
list.of.functions1[[a]] <- functionGen1(a)
list.of.functions2[[a]] <- functionGen2(a)
}
rm(a)
# Throws an error "Error in print(aNumber) : object 'a' not found"
list.of.functions1[[1]]()
# Prints 1
list.of.functions2[[1]]()
# Prints 2
list.of.functions2[[2]]()
# However this produces a list of functions which work
list.of.functions1b <- lapply(c(1:2), functionGen1)
A more minimal example:
functionGen1 <- function(aNumber) {
printNumber <- function() {
print(aNumber)
}
return(printNumber)
}
a <- 1
myfun <- functionGen1(a)
rm(a)
myfun()
#Error in print(aNumber) : object 'a' not found
Your question is not about namespaces (that's a concept related to packages), but about variable scoping and lazy evaluation.
Lazy evaluation means that function arguments are only evaluated when they are needed. Until you call myfun it is not necessary to evaluate aNumber = a. But since a has been removed then, this evaluation fails.
The usual solution is to force evaluation explicitly as you do with your functionGen2 or, e.g.,
functionGen1 <- function(aNumber) {
force(aNumber)
printNumber <- function() {
print(aNumber)
}
return(printNumber)
}
a <- 1
myfun <- functionGen1(a)
rm(a)
myfun()
#[1] 1
I want to make an R function that returns a list of functions that each operate on a different part of a list. However, because of R's rules on scope, this seems impossible. Here's an example:
functiontest = function() {
foo = list()
for(i in 1:3) {
fixer = function(s) { return(
function() {
return(s)
}
)}
foo[[i]] = fixer(i)
}
return(foo)
}
functiontest()[[2]]() #returns 3
Even killing off the named lambda "fixer" and using an immediate function doesn't save me:
functiontest = function() {
foo = list()
for(i in 1:3) {
foo[[i]]=(function(s) { return(
function() {
return(s)
}
)})(i)
}
return(foo)
}
functiontest()[[2]]() #returns 3
I want this to return 2. How can I refactor this code so that this will happen?
Thanks.
First of all, the problem you have faced has nothing to do with the scoping. Second, I don't think it is a good idea to call something idiotic just because you don't understand it.
In your example, all functions in a list return the same value because of the lazy evaluation mechanism in R. See help(force), which specifically addresses your question. In short, you need to force evaluation of the function at the time you call it, which can be done by adding force:
functiontest = function() {
foo = list()
for(i in 1:3) {
fixer = function(s) {
force(s) ### <<<---This is the only difference from your code ###
return(
function() {
return(s)
}
)}
foo[[i]] = fixer(i)
}
return(foo)
}
or, using a more concise syntax:
functiontest <- function() lapply(1:3, function(s) {s; function() s})
Example of use:
> L <- functiontest()
> L[[1]]()
[1] 1
> L[[2]]()
[1] 2
> L[[3]]()
[1] 3
I have a function who's task is to create a variable in the parent object. What I want is for the function to create the variable at the level at which it's called.
createVariable <- function(var.name, var.value) {
assign(var.name,var.value,envir=parent.frame())
}
# Works
testFunc <- function() {
createVariable("testVar","test")
print(testVar)
}
# Doesn't work
testFunc2 <- function() {
testFunc()
print(testVar)
}
> testFunc()
[1] "test"
> testFunc2()
[1] "test"
Error in print(testVar) : object 'testVar' not found
I'm wondering if there's any way to do this without creating the variable in the global environment scope.
Edit: Is there also a way to unit test that a variable has been created?
Try this:
createVariable <- function(var.name, var.value) {
assign(var.name,var.value,envir=parent.env(environment()))
}
Edit:
Some more details here and here.
With the initial solution, the variable is created in the global env because parent.env is the environment in which the function is defined and the createVariable function is defined in the global environment.
You might also want to try assign(var.name,var.value,envir=as.environment(sys.frames()[[1]])), which will create it in the highest test function calling createVariable in your example (first one on the call stack), in that case however, you will need to remove print(testVar) from testFunc when you call testFunc2 because the variable only be created in the environment of testFunc2, not testFunc. I don't know if that's what you mean by at the level at which it's called.
If you run this:
createVariable <- function(var.name, var.value) {
assign(var.name,var.value,envir=as.environment(sys.frames()[[1]]))
print("creating")
}
testFunc <- function() {
createVariable("testVar","test")
print("func1")
print(exists("testVar"))
}
testFunc2 <- function() {
testFunc()
print("func2")
print(exists("testVar"))
}
testFunc()
testFunc2()
You get
> testFunc()
[1] "creating"
[1] "func1"
[1] TRUE
> testFunc2()
[1] "creating"
[1] "func1"
[1] FALSE
[1] "func2"
[1] TRUE
Which means testVar is in testFun2's environment, not in testFunc's. Creating a new environment as others say might be safer.
You need the parent environment to do this, not the calling environment:
createVariable <- function(var.name, var.value) {
#use parent.env(environment())
assign(var.name,var.value,envir=parent.env(environment()))
}
> testFunc()
[1] "test"
> testFunc2()
[1] "test"
[1] "test"
Why do you want to do this? Using assign can lead to hard to find bugs and other problems.
What might be a better approach is to create a new environment just before calling your function of interest. Then in your function assign into that new environment (best if the environment is passed to the function, but can use lexical scoping as well to access it). Then when the function returns you will have the new variable in the environment:
createVariable <- function(var.name, var.value, env) {
env[[var.name]] <- var.value
}
testfunc <- function() {
tmpenv <- new.env()
createVariable('x', 1:10, tmpenv)
print(ls())
print(ls(env=tmpenv))
print(tmpenv$x)
}
If createVariable were defined inside of testfunc then it could access tmpenv directly without needing it passed down (but passing down is safest when possible).
This version of createVariable could even be used more directly to assign in the environment of the calling function (but this becomes more dangerous, too easy to overwrite something in the current environment, or trying to access something by the wrong name due to a small typo):
testfunc2 <- function() {
createVariable('y', 5:1, environment())
print(ls())
print(y)
}
If you create a new environment and assign the value to it:
my.env <- new.env()
my.env$my.object <- runif(1)
Then call it using get:
> my.object # not found in global env
Error: object 'my.object' not found
> get("my.object", envir = my.env)
[1] 0.07912637
For your function:
createVariable <- function(env.name, var.name, var.value) {
env.name <- new.env()
assign(var.name, var.value, envir = env.name)
}