W R, Jak zrobić zmienne wewnątrz funkcji dostępne dla funkcji niższego poziomu wewnątrz tej funkcji?(z, dołącz, środowisko)

Aktualizacja 2 @G. Grothendieck zamieścił dwa podejścia. Drugi to zmiana środowiska funkcji wewnątrz funkcji. To rozwiązuje mój problem zbyt wielu replikatów kodowania. Nie jestem pewien, czy jest to dobra metoda, aby przejść przez sprawdzenie CRAN podczas tworzenia moich skryptów do pakietu. Zaktualizuję ponownie, gdy będę miał pewne wnioski.

Update

Próbuję przekazać wiele zmiennych argumentów wejściowych do f2 i nie chcę indeksuje każdą zmienną wewnątrz funkcji jako env$c, env$d, env$calls, dlatego próbowałem użyć with w f5 i f6 (zmodyfikowanego f2). Jednak {[11] } nie działa z with wewnątrz {}, przeniesienie assign Na Zewnątrz with da radę, ale w moim prawdziwym przypadku mam kilka assign wewnątrz with wyrażeń, których Nie wiem, jak je łatwo przenieść z with funkcji.

Oto przykład:

## In the <environment: R_GlobalEnv>
a <- 1
b <- 2
f1 <- function(){
    c <- 3
d <- 4
f2 <- function(P){
    assign("calls", calls+1, inherits=TRUE)
    print(calls)
    return(P+c+d)
 }
calls <- 0
v <- vector()
for(i in 1:10){
    v[i] <- f2(P=0)
    c <- c+1
    d <- d+1
  }
 return(v)
}
f1()

Funkcja f2 znajduje się wewnątrz f1, gdy wywołana jest f2, szuka zmiennych calls,c,d w środowisku environment(f1). Tego właśnie chciałem.

Jednakże, gdy chcę używać f2 również w innych funkcjach, zdefiniuję tę funkcję w środowisku globalnym, nazwijmy ją f4.

f4 <- function(P){
  assign("calls", calls+1, inherits=TRUE)
  print(calls)
  return(P+c+d)
}

To nie zadziała, ponieważ będzie szukać calls,c,d w środowisku globalnym zamiast wewnątrz funkcji, do której funkcja jest wywoływana. Na przykład:

f3 <- function(){
  c <- 3
  d <- 4
  calls <- 0
  v <- vector()
  for(i in 1:10){
    v[i] <- f4(P=0) ## or replace here with f5(P=0)
    c <- c+1
    d <- d+1
  }
  return(v)
}
f3()

Bezpieczny sposób należy zdefiniować calls,c,d w argumentach wejściowych f4, a następnie przekazać te parametry do f4. Jednak w moim przypadku jest zbyt wiele zmiennych do przekazania do tej funkcji {[25] } i byłoby lepiej, gdybym mógł przekazać ją jako środowisko i powiedzieć f4 nie szukać w środowisku globalnym (environment(f4)), tylko zajrzeć do wnętrza environment kiedy f3 jest wywołana.

Sposób, w jaki go teraz rozwiązuję, polega na użyciu środowiska jako listy i użyciu funkcji with.

f5 <- function(P,liste){
  with(liste,{
     assign("calls", calls+1, inherits=TRUE)
     print(calls)
     return(P+c+d)
     }
  )
}
f3 <- function(){
  c <- 3
  d <- 4
  calls <- 0
  v <- vector()
  for(i in 1:10){
    v[i] <- f5(P=0,as.list(environment())) ## or replace here with f5(P=0)
    c <- c+1
    d <- d+1
  }
  return(v)
}
f3()

Jednak teraz assign("calls", calls+1, inherits=TRUE) nie działa tak jak powinno, ponieważ assign nie modyfikuje oryginalny obiekt. Zmienna calls jest połączona z funkcją optymalizacyjną, gdzie funkcją celu jest f5. Z tego powodu używam assign zamiast przekazywania calls jako argumentów wejściowych. Używanie attach również nie jest dla mnie jasne. Oto mój sposób na poprawienie problemu assign:

f7 <- function(P,calls,liste){
  ##calls <<- calls+1
  ##browser()
  assign("calls", calls+1, inherits=TRUE,envir = sys.frame(-1))
  print(calls)
  with(liste,{
    print(paste('with the listed envrionment, calls=',calls))
    return(P+c+d)
  }
  )
}
########
##################
f8 <- function(){
  c <- 3
  d <- 4
  calls <- 0
  v <- vector()
  for(i in 1:10){
    ##browser()
    ##v[i] <- f4(P=0) ## or replace here with f5(P=0)
    v[i] <- f7(P=0,calls,liste=as.list(environment()))
    c <- c+1
    d <- d+1
  }
  f7(P=0,calls,liste=as.list(environment()))
  print(paste('final call number',calls))
  return(v)
}
f8()

Nie jestem pewien, jak to zrobić w R. Czy jestem w dobrym kierunku, zwłaszcza gdy przejeżdżam przez Cran check? Ma ktoś jakieś wskazówki na ten temat?

Author: Zhenglei, 2013-01-18

4 answers

(1) Podaj środowisko rozmówcy. Możesz jawnie przekazać środowisko nadrzędne i indeks do niego. Spróbuj tego:

f2a <- function(P, env = parent.frame()) {
    env$calls <- env$calls + 1
    print(env$calls)
    return(P + env$c + env$d)
}

a <- 1
b <- 2
# same as f1 except f2 removed and call to f2 replaced with call to f2a
f1a <- function(){
    c <- 3
    d <- 4
    calls <- 0
    v <- vector()
    for(i in 1:10){
        v[i] <- f2a(P=0)
        c <- c+1
        d <- d+1
      }
     return(v)
}
f1a()

(2) zresetowanie środowiska wywołanego przez funkcję możemy zresetować środowisko f2b w f1b Jak pokazano tutaj:

f2b <- function(P) {
    calls <<- calls + 1
    print(calls)
    return(P + c + d)
}

a <- 1
b <- 2
# same as f1 except f2 removed, call to f2 replaced with call to f2b
#  and line marked ## at the beginning is new
f1b <- function(){
    environment(f2b) <- environment() ##
    c <- 3
    d <- 4
    calls <- 0
    v <- vector()
    for(i in 1:10){
        v[i] <- f2b(P=0)
        c <- c+1
        d <- d+1
      }
     return(v)
}
f1b()

(3) Makro za pomocą eval.rodzic (substytut (...)) Jeszcze innym podejściem jest zdefiniowanie makro-podobnego konstruktu, który skutecznie wtryskuje ciało f2c inline do f1c1. Tutaj f2c jest tym samym co f2b z wyjątkiem linii calls <- calls + 1 (Nie <<- potrzebne) i owinięcia całego ciała w eval.parent(substitute({...})). f1c jest tym samym co f1a, z tym że wywołanie do f2a zostaje zastąpione wywołaniem do f2c.

f2c <- function(P) eval.parent(substitute({
    calls <- calls + 1
    print(calls)
    return(P + c + d)
}))

a <- 1
b <- 2
f1c <- function(){
    c <- 3
    d <- 4
    calls <- 0
    v <- vector()
    for(i in 1:10){
        v[i] <- f2c(P=0)
        c <- c+1
        d <- d+1
      }
     return(v)
}
f1c()

(4) defmacro jest to prawie to samo co ostatnie rozwiązanie, z wyjątkiem tego, że używa defmacro w pakiecie gtools do definiowania makra, a nie robienia tego samemu. (Zobacz także pakiet rcmdr dla innej wersji defmacro.) Ze względu na sposób działania defmacro musimy również przejść calls ale ponieważ jest to makro, a nie Funkcja, to po prostu mówi mu, aby zastąpił calls in i nie jest to to samo, co przekazanie calls do funkcji.

library(gtools)

f2d <- defmacro(P, calls, expr = {
    calls <- calls + 1
    print(calls)
    return(P + c + d)
})

a <- 1
b <- 2
f1d <- function(){
    c <- 3
    d <- 4
    calls <- 0
    v <- vector()
    for(i in 1:10){
        v[i] <- f2d(P=0, calls)
        c <- c+1
        d <- d+1
      }
     return(v)
}
f1d()
 31
Author: G. Grothendieck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-12 19:15:33

Ogólnie rzecz biorąc, powiedziałbym, że każda zmienna, która jest potrzebna wewnątrz funkcji, powinna być przekazywana przez jej argumenty. Ponadto, jeśli jego wartość jest potrzebna później, przekazujesz ją z powrotem z funkcji. Nie robienie tego może dość szybko prowadzić do dziwnych wyników, np. co jeśli istnieje wiele funkcji definiujących zmienną x, której należy użyć. Jeśli ilość zmiennych jest większa, tworzy się dla nich niestandardową strukturę danych, np. umieszczając je na liście nazwanej.

 2
Author: Paul Hiemstra,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-18 13:59:20

Można również użyć funkcji, która redefiniuje inne funkcje w określonym środowisku.

test_var <- "global"

get_test_var <- function(){
  return(test_var)
}

some_function <- function(){
  test_var <- "local"
  return(get_test_var()) 

}

some_function() # Returns "global". Not what we want here...

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

some_function2 <- function(){
  test_var <- "local"
  # define function locally
  get_test_var2 <- function(){
    return(test_var)
  }
  return(get_test_var2()) 
}

some_function2() # Returns "local", but 'get_test_var2' can't be used in other places.

# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

add_function_to_envir <- function(my_function_name, to_envir) {
  script_text <- capture.output(eval(parse(text = my_function_name)))
  script_text[1] <- paste0(my_function_name, " <- ", script_text[1])
  eval(parse(text = script_text), envir = to_envir)
}

some_function3 <- function(){
  test_var <- "local"
  add_function_to_envir("get_test_var", environment()) 
  return(get_test_var()) 
}

some_function3() # Returns "local" and we can use 'get_test_var' from anywhere.

Tutaj add_function_to_envir(my_function_name, to_envir) przechwytuje skrypt funkcji, parsuje i ponownie ocenia go w nowym środowisku.

Uwaga: nazwa funkcji dla my_function_name musi być w cudzysłowach.

 1
Author: Dante Bortone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-26 15:07:20

Kiedykolwiek używam funkcji zagnieżdżonych i nie przekazuję zmiennych jako argumentów, ale zamiast tego przekazuję je za pomocą ..., używam następującej funkcji we wszystkich zagnieżdżonych funkcjach, aby pobrać zmienne ze środowiska nadrzędnego.

LoadVars <- function(variables, ...){
  for (var in 1:length(variables)) {
    v <- get(variables[var], envir = parent.frame(n=2))
    assign(variables[var], v, envir = parent.frame(n=1))
  }
}

Wewnątrz zagnieżdżonej funkcji, i następnie LoadVars(c("foo", "bar")).

To podejście jest użyteczne w tym sensie, że przekazujesz tylko potrzebne zmienne, podobnie jak przekazujesz zmienne za pomocą argumentów.

Podejście 2

Jednakże, jest to proste, aby przepisać tę funkcję, aby załadować wszystkie zmienne z funkcji nadrzędnej - lub wyżej w razie potrzeby, po prostu zwiększ wartość n w parent.frame z jej pierwotnej wartości 2.

LoadVars <- function(){
  variables <- ls(envir = parent.frame(n=2))

  for (var in 1:length(variables)) {
    v <- get(variables[var], envir = parent.frame(n=2))
    assign(variables[var], v, envir = parent.frame(n=1))
  }
}

Przykład

a <- 1

A <- function(...){
  b <- 2
  printf("A, a = %s", a)
  printf("A, b = %s", b)
  B()
}

B <- function(...){
  LoadVars()
  printf("B, a = %s", a)
  printf("B, b = %s", b)
}

A()

Jeśli nie załadujesz zmiennych w B, to B jest w stanie załadować a, ponieważ jest to globalna zmienna środowiskowa, ale nie b, która znajduje się w A().

Wyjście:

[1] "A, a = 1"
[1] "A, b = 2"
[1] "B, a = 1"
[1] "B, b = 2"
 0
Author: Christian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-03-15 09:56:59