обіцянка вже під оцінкою: рекурсивна посилання на аргумент за замовчуванням або попередні проблеми?


143

Ось мій R-код. Функції визначаються як:

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)
}

Помилка запуску:

> test ()
Помилка тесту ():
обіцянка вже під оцінкою: рекурсивна посилання на аргумент за замовчуванням або попередні проблеми?

Якщо я підміняю визначення fв цьому g, то помилка усувається.

Мені було цікаво, в чому помилка? Як це виправити, якщо не замінити означення fв цьому g? Дякую!


Оновлення:

Дякую! Два питання:

(1) якщо функція testдодатково бере аргумент f, ви додасте щось подібне test <- function(g.=g, T=1, f..=f){ g.(1,T, f.=f..) }? У випадках із більшою кількістю рекурсій, чи є хорошою та безпечною практикою додавання більше . ?

(2) якщо f, наприклад , аргумент, що не функціонує, g <- function(x, T, f=f){ exp(-f*x/T) }чи test <- function(g.=g, T=1, f=f){ g.(1,T, f=f.) }буде використання одного і того ж імені як для формальних, так і для фактичних нефункціональних аргументів хорошою та безпечною практикою, або це може спричинити певні проблеми?

Відповіді:


159

Формальні аргументи форми x=xвикликають це. Усуваючи два випадки, коли вони виникають, ми отримуємо:

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
Дякую! Два питання (1), якщо тест функції додатково бере аргумент для f , додасте ви щось на зразок тесту <- функція (g. = G, T = 1, f .. = f) {g. (1, T, f. = f ..)} ? У випадках із більшою кількістю рекурсій, чи є хорошою та безпечною практикою додавання більше . ? (2) якщо f - нефункціональний аргумент, наприклад g <- функція (x, T, f = f) {exp (-f x / T)} * і тест <- функція (g. = G, T = 1, f = f) {g. (1, T, f = f.)} , Чи використовуватиме одне й те саме ім’я як для формальних, так і для фактичних нефункціональних аргументів хорошою та безпечною практикою чи це може спричинити певні проблеми?
Тім

16
Будь-які інші рішення? Я передаю деякі аргументи досить глибоко в ланцюзі функцій (близько 5 рівнів), і це рішення може стати .....cumbersome. :)
Роман Луштрик

2
@ RomanLuštrik Якщо ви передаєте аргументи вниз і ви можете сміливо ігнорувати деякі з них, тоді перегляньте, використовуючи еліпси ...або список, щоб передати аргументи вниз по ланцюгу функцій. Це набагато гнучкіше (для добрих і хворих), ніж попередньо все визначати. Вам може просто потрібно додати кілька чеків, щоб переконатися, що ваші оригінальні аргументи в еліпсах (або списку) є розумними.
russellpierce

2
Інший варіант тут полягає в явній спробі знайти аргументи в батьківському кадрі, обходячи випадкове вимушення активної обіцянки - наприклад get("f", envir = parent.frame()).
Кевін Ушей

1
Єдина вимога - ви не використовуєте однойменне ліворуч та праворуч. Крім цього, це просто стиль.
G.

13

Якщо ви конкретизуєте контекст оцінки аргументів, ви уникаєте однойменної проблеми:

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
Це кращий спосіб, я думаю, що конкретніше середовище буде більш зрозумілим
хмари обчислюють

1

Мені подобається відповідь Г. Гротендіка , але мені було цікаво, що у вашому випадку простіше не включати назви функцій у параметри функцій, як це:

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

Як вже було сказано, проблема виникає в тому, що аргумент функції визначений як сам. Однак я хочу додати пояснення, чому це проблема, тому що розуміння, яке призвело мене до більш легкого (для мене) способу уникнути проблеми: просто вкажіть аргумент у виклику замість визначення.

Це не працює:

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

але це працює:

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

Аргументи функції існують у власному локальному середовищі.

R шукає змінні спочатку в локальному середовищі, потім у глобальному середовищі. Це так само, як всередині функції змінна може мати те саме ім'я, що і змінна в глобальному середовищі, і R буде використовувати локальне визначення.

Означення аргументів функції формують своє власне локальне середовище, тому ви можете мати значення аргументів за замовчуванням на основі інших значень аргументів, наприклад

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

Отже, ви не можете ВИЗНАЧИТИСЯ функцію як, my.function <- function(x = x){}але ви можете ЗАКАЗАТИ функцію, використовуючи my.function(x = x). Коли ви визначаєте функцію, R заплутається, оскільки він знаходить аргумент x =як локальне значення x, але при виклику функції R знаходить x = 4у локальному середовищі, з якого ви телефонуєте.

Отже, крім виправлення помилки, змінивши ім'я аргументу або чітко вказавши середовище, як зазначено в інших відповідях, ви також можете просто вказати, що x=xколи ви викликаєте функцію замість того, коли ви її визначаєте. Для мене, уточнюючи, що x=xв дзвінку було найкращим рішенням, оскільки він не передбачає додаткового синтаксису або накопичення все більше і більше змінних імен.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.