obietnica już w trakcie oceny: rekurencyjne odwołanie do domyślnego argumentu lub wcześniejsze problemy?


143

Oto mój kod R. Funkcje są zdefiniowane jako:

f <- function(x, T) {
  10 * sin(0.3 * x) * sin(1.3 * x ^ 2) + 0.001 * x ^ 3 + 0.2 * x + 80
}

g <- function(x, T, f=f) {
  exp(-f(x) / T)
}

test <- function(g=g, T=1) { 
  g(1, T)
}

Błąd działania to:

> test ()
Błąd w test ():
obietnica już w trakcie oceny: rekurencyjne odwołanie do domyślnego argumentu lub wcześniejsze problemy?

Jeśli zastąpię definicję w definicji fof g, to błąd zniknie.

Zastanawiałem się, co to za błąd? Jak go poprawić, jeśli nie zastępują definicji fw tym z g? Dzięki!


Aktualizacja:

Dzięki! Dwa pytania:

(1) jeśli funkcja testprzyjmuje argument za f, czy dodasz coś takiego test <- function(g.=g, T=1, f..=f){ g.(1,T, f.=f..) }? W przypadkach z większą liczbą rekurencji dobrym i bezpiecznym sposobem jest dodawanie kolejnych . ?

(2) jeśli na przykład fjest argumentem niefunkcjonalnym g <- function(x, T, f=f){ exp(-f*x/T) }i test <- function(g.=g, T=1, f=f){ g.(1,T, f=f.) }czy używanie tej samej nazwy zarówno dla formalnych, jak i faktycznych argumentów niefunkcjonalnych będzie dobrą i bezpieczną praktyką, czy może spowodować potencjalne problemy?

Odpowiedzi:


159

x=xPowodują to formalne argumenty formularza . Eliminując dwa przypadki, w których występują, otrzymujemy:

f <- function(x, T) {
   10 * sin(0.3 * x) * sin(1.3 * x^2) + 0.001 * x^3 + 0.2 * x + 80 
}

g <- function(x, T, f. = f) {  ## 1. note f.
   exp(-f.(x)/T) 
}

test<- function(g. = g, T = 1) {  ## 2. note g.
   g.(1,T) 
}

test()
## [1] 8.560335e-37

2
Dzięki! Dwa pytania (1), jeśli funkcja test dalej przyjmuje argument dla f , czy dodasz coś takiego jak test <- funkcja (g. = G, T = 1, f .. = f) {g. (1, T, f. = f ..)} ? W przypadkach z większą liczbą rekurencji dobrym i bezpiecznym sposobem jest dodawanie kolejnych . ? (2) jeśli f jest argumentem niebędącym funkcją, na przykład g <- function (x, T, f = f) {exp (-f x / T)} * i test <- function (g. = G, T = 1, f = f) {g. (1, T, f = f.)} , Czy używanie tej samej nazwy zarówno dla formalnych, jak i rzeczywistych niefunkcjonalnych argumentów będzie dobrą i bezpieczną praktyką, czy może spowodować potencjalne problemy?
Tim

16
Jakieś inne rozwiązania? Podaję kilka argumentów dość głęboko w łańcuchu funkcji (około 5 poziomów) i takim rozwiązaniem może się stać .....cumbersome. :)
Roman Luštrik

2
@ RomanLuštrik Jeśli przekazujesz argumenty w dół i możesz bezpiecznie zignorować niektóre z nich, spójrz na użycie wielokropków ...lub listy do przekazywania argumentów w dół łańcucha funkcji. Jest dużo bardziej elastyczny (na dobre i na złe) niż wstępne definiowanie wszystkiego. Może się okazać, że po prostu będziesz musiał dodać kilka sprawdzeń, aby upewnić się, że oryginalne argumenty w elipsach (lub liście) są rozsądne.
russellpierce

2
Inną opcją jest tutaj jawna próba znalezienia argumentów w ramce nadrzędnej, z pominięciem przypadkowego wymuszenia aktywnej obietnicy - np get("f", envir = parent.frame()).
Kevin Ushey

1
Jedynym wymaganiem jest, aby nie używać tej samej nazwy po lewej i prawej stronie. Poza tym to tylko styl.
G. Grothendieck

13

Jeśli określisz kontekst oceny argumentów, unikniesz problemu o tej samej nazwie:

f <- function(x) {
  10 * sin(0.3 * x) * sin(1.3 * x ^ 2) + 0.001 * x ^ 3 + 0.2 * x + 80
}
g <- function(x, t=1, f=parent.frame()$f) {
  exp(-f(x) / t)
}
test <- function(g=parent.frame()$g, t=1) { 
  g(1,t)
}
test()
[1] 8.560335e-37

2
Jest to lepszy sposób, myślę, że środowisko jest bardziej przejrzyste
oblicza chmury z

1

Podoba mi się odpowiedź G. Grothendiecka , ale zastanawiałem się, czy w twoim przypadku prostsze jest nie umieszczanie nazw funkcji w parametrach funkcji, na przykład:

f <- function(x, T) {
  10 * sin(0.3 * x) * sin(1.3 * x^2) + 0.001 * x^3 + 0.2 * x + 80 
}
g <- function(x, T) {
  exp(-f(x)/T) 
}
test<- function(T = 1) {
  g(1,T)
}
test()
## [1] 8.560335e-37

1

Jak już wspomniano, problem wynika z posiadania argumentu funkcji zdefiniowanego jako sam. Chciałbym jednak dodać wyjaśnienie, dlaczego jest to problem, ponieważ zrozumienie, które doprowadziło mnie do łatwiejszego (dla mnie) sposobu uniknięcia problemu: po prostu podaj argument w wywołaniu zamiast definicji.

To nie działa:

x = 4
my.function <- function(x = x){} 
my.function() # recursive error!

ale to działa:

x = 4
my.function <- function(x){} 
my.function(x = x) # works fine!

Argumenty funkcji istnieją w ich własnym środowisku lokalnym.

R szuka zmiennych najpierw w środowisku lokalnym, a następnie w środowisku globalnym. To jest tak, jak w przypadku, gdy zmienna wewnątrz funkcji może mieć taką samą nazwę jak zmienna w środowisku globalnym, a R użyje definicji lokalnej.

Posiadanie definicji argumentów funkcji w ich własnym środowisku lokalnym jest powodem, dla którego można mieć domyślne wartości argumentów oparte na innych wartościach argumentów, takich jak

my.function <- function(x, two.x = 2 * x){}

Dlatego właśnie nie możesz DEFINIOWAĆ funkcji jako, my.function <- function(x = x){}ale możesz WYWOŁAĆ funkcję za pomocą my.function(x = x). Kiedy definiujesz funkcję, R jest zdezorientowany, ponieważ znajduje argument x =jako lokalną wartość x, ale kiedy wywołujesz funkcję, R znajduje się x = 4w lokalnym środowisku, z którego dzwonisz.

Więc oprócz naprawienia błędu poprzez zmianę nazwy argumentu lub jawne określenie środowiska, jak wspomniano w innych odpowiedziach, możesz również po prostu określić to x=xpodczas wywoływania funkcji, a nie podczas jej definiowania. Dla mnie określenie, że x=xw wywołaniu było najlepszym rozwiązaniem, ponieważ nie wymaga dodatkowej składni ani gromadzenia coraz większej liczby nazw zmiennych.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.