Reputation: 2665
Is it possible to have a variable with a dynamic binding in R? Say, to have something like this to work:
f1a <- function() {
x <- 1
f2()
}
f1b <- function() {
x <- 2
f2()
}
f2 <- function() {
f3()
}
f3 <- function() {
# print x
}
f1a() # prints 1
f1b() # prints 2
I can pass x
down the stack explicitly, but that is not what I'm looking for. I'm asking about a possibility for a dynamic binding. I understand that many people would say passing x
explicitly is always preferable, but suppose I cannot change the definition of f2
(I can change f1a
, f1b
and f3
). I can have a global variable, of course, but I'd rather use a dynamic binding if possible.
Upvotes: 1
Views: 150
Reputation: 1108
f1a <- function() {
x <- 1
f2()
}
f1b <- function() {
x <- 2
f2()
}
f2 <- function() {
f3()
}
f3 <- function() {
get("x", envir = parent.frame(2))
}
Simply move the environment in which f3
is looking for the variable x
.
> f1a()
[1] 1
> f1b()
[1] 2
get("x", envir = parent.frame(2))
looks for a variable with symbol x
in an environment two frames up the call stack:
f3 <- function() {
get("x", envir = parent.frame(2))
rlang::trace_back()
}
> f1a()
█
1. └─global::f1a()
2. └─global::f2()
3. └─global::f3()
If you need to iterate up an entire call stack until the first of a symbol is encountered, simply use dynGet()
:
f1a <- function() {
x <- 1
f2()
}
f1b <- function() {
x <- 2
f2()
}
f2 <- function() {
f3()
}
f3 <- function() {
f4()
}
f4 <- function() {
f5()
}
f5 <- function() {
dynGet("x")
}
> f1a()
[1] 1
> f1b()
[1] 2
This will stop at the first x
encountered moving up the call stack from the calling environment. So if we adjust f2
:
f2 <- function() {
x <- 3
f3()
}
> f1a()
[1] 3
> f1b()
[1] 3
Again, the full call stack can be seen with rlang::trace_back
:
f5 <- function() {
dynGet("x")
rlang::trace_back()
}
> f1a()
█
1. └─global::f1a()
2. └─global::f2()
3. └─global::f3()
4. └─global::f4()
5. └─global::f5()
Upvotes: 5