Возможно ли иметь переменную с динамической привязкой в ​​R? Скажем, чтобы работало что-то вроде этого:

f1a <- function() {
  x <- 1
  f2()
}

f1b <- function() {
  x <- 2
  f2()
}

f2 <- function() {
  f3()
}

f3 <- function() {
  # print x
}

f1a() # prints 1
f1b() # prints 2

Я могу явно передать x вниз по стеку, но это не то, что я ищу. Я спрашиваю о возможности динамической привязки. Я понимаю, что многие люди сказали бы, что передача x явным образом всегда предпочтительнее, но предположим, что я не могу изменить определение f2 (я могу изменить f1a, f1b и f3). Конечно, у меня может быть глобальная переменная, но я бы предпочел использовать динамическую привязку, если это возможно.

r
1
Andrei 30 Сен 2020 в 10:12

1 ответ

Лучший ответ
f1a <- function() {
  x <- 1
  f2()
}

f1b <- function() {
  x <- 2
  f2()
}

f2 <- function() {
  f3()
}

f3 <- function() {
  get("x", envir = parent.frame(2))
}

Просто переместите среду, в которой f3 ищет переменную x.

> f1a()
[1] 1
> f1b()
[1] 2

get("x", envir = parent.frame(2)) ищет переменную с символом x в среде двумя кадрами вверх по стеку вызовов:

f3 <- function() {
  get("x", envir = parent.frame(2))
  rlang::trace_back()
}

> f1a()
    █
 1. └─global::f1a()
 2.   └─global::f2()
 3.     └─global::f3()

Если вам нужно перебрать весь стек вызовов до тех пор, пока не встретится первый символ, просто используйте 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

Это остановится при первом обнаруженном x перемещении вверх по стеку вызовов из вызывающей среды. Итак, если мы настроим f2:

f2 <- function() {
  x <- 3
  f3()
}

> f1a()
[1] 3
> f1b()
[1] 3

Опять же, полный стек вызовов можно увидеть с помощью 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()
5
Dylan Russell 30 Сен 2020 в 07:44