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


149

Вот мой код 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 ()
Ошибка в 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.) }будет ли использование одного и того же имени как для формальных, так и для фактических нефункциональных аргументов хорошей и безопасной практикой, или это может вызвать некоторые потенциальные проблемы?

Ответы:


168

Формальные аргументы формы 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 , добавите ли вы что-нибудь вроде test <- function (g. = G, T = 1, f .. = f) {g. (1, T, 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.)} , Будет ли использование одного и того же имени для формальных и фактических нефункциональных аргументов хорошей и безопасной практикой, или это может вызвать некоторые потенциальные проблемы?
Тим

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

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

3
Другой вариант здесь - явно попытаться найти аргументы в родительском фрейме, минуя случайное принудительное выполнение активного обещания - например get("f", envir = parent.frame()).
Кевин Уши

1
Единственное требование - не использовать одно и то же имя слева и справа. В остальном это просто стиль.
Г. Гротендик

15

Если вы укажете контекст оценки аргумента, вы избежите проблемы с тем же именем:

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
Это лучший способ, я думаю, уточните, что окружающая среда более чистая
cloudscomputes

5

Как уже упоминалось, проблема возникает из-за того, что аргумент функции определен сам по себе. Однако я хочу добавить объяснение, почему это проблема, потому что понимание, которое привело меня к более легкому (для меня) способу избежать проблемы: просто укажите аргумент в вызове вместо определения.

Это не работает:

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


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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.