janji sudah di bawah evaluasi: referensi argumen default rekursif atau masalah sebelumnya?

143

Ini kode R saya. Fungsi didefinisikan sebagai:

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

Kesalahan yang berjalan adalah:

> test ()
Kesalahan dalam pengujian ():
janji sudah dalam evaluasi: referensi argumen default rekursif atau masalah sebelumnya?

Jika saya mengganti definisi dari fitu g, maka kesalahan hilang.

Saya bertanya-tanya apa kesalahannya? Bagaimana cara memperbaikinya jika tidak mengganti definisi fdi dalam dari itu g? Terima kasih!


Memperbarui:

Terima kasih! Dua pertanyaan:

(1) jika fungsi testlebih lanjut membutuhkan argumen f, apakah Anda akan menambahkan sesuatu seperti test <- function(g.=g, T=1, f..=f){ g.(1,T, f.=f..) }? Dalam kasus dengan lebih banyak rekursi, apakah ini praktik yang baik dan aman menambahkan lebih banyak . ?

(2) jika fargumen non-fungsi, misalnya g <- function(x, T, f=f){ exp(-f*x/T) }dan test <- function(g.=g, T=1, f=f){ g.(1,T, f=f.) }, akan menggunakan nama yang sama untuk argumen non-fungsional formal dan aktual merupakan praktik yang baik dan aman atau dapat menyebabkan beberapa potensi masalah?

Tim
sumber

Jawaban:

159

Argumen formal dari formulir x=xmenyebabkan ini. Menghilangkan dua contoh di mana mereka terjadi kita dapatkan:

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
G. Grothendieck
sumber
2
Terima kasih! Dua pertanyaan (1) jika tes fungsi lebih lanjut mengambil argumen untuk f , apakah Anda akan menambahkan sesuatu seperti tes <- function (g. = G, T = 1, f .. = f) {g. (1, T, f. = f ..)} ? Dalam kasus dengan lebih banyak rekursi, apakah ini praktik yang baik dan aman menambahkan lebih banyak . ? (2) jika f adalah argumen non-fungsi, misalnya g <- function (x, T, f = f) {exp (-f x / T)} * dan uji <- function (g. = G, T = 1, f = f) {g. (1, T, f = f.)} , Akan menggunakan nama yang sama untuk argumen non-fungsional formal dan aktual sebagai praktik yang baik dan aman atau dapat menyebabkan beberapa masalah potensial?
Tim
16
Ada solusi lain? Saya menyampaikan beberapa argumen yang cukup mendalam di rantai fungsi (sekitar 5 level), dan solusi ini bisa menjadi .....cumbersome. :)
Roman Luštrik
2
@ RomanLuštrik Jika Anda meneruskan argumen ke bawah dan Anda dapat mengabaikan beberapa argumen dengan aman, maka lihat menggunakan elips ...atau daftar untuk meneruskan argumen ke rantai fungsi. Ini jauh lebih fleksibel (baik dan buruk) daripada menentukan segalanya sebelumnya. Anda mungkin akhirnya perlu menambahkan beberapa cek untuk memastikan argumen asli Anda di elips (atau daftar) masuk akal.
russellpierce
2
Pilihan lain di sini adalah secara eksplisit mencoba menemukan argumen dalam kerangka induk, melewati pemaksaan paksa dari janji aktif - mis get("f", envir = parent.frame()).
Kevin Ushey
1
Satu-satunya persyaratan adalah Anda tidak menggunakan nama yang sama di sisi kiri dan kanan. Selain itu hanya gaya.
G. Grothendieck
13

Jika Anda menetapkan konteks evaluasi argumen, Anda menghindari masalah dengan nama yang sama:

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
xm1
sumber
2
Ini adalah cara yang lebih baik, saya pikir tentukan lingkungan lebih jernih
cloudscomputes
1

Saya suka jawaban G. Grothendieck , tapi saya bertanya-tanya itu lebih sederhana dalam kasus Anda untuk tidak memasukkan nama fungsi dalam parameter fungsi, seperti ini:

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
t4x0n
sumber
1

Seperti yang telah disebutkan, masalahnya berasal dari memiliki argumen fungsi yang didefinisikan sebagai dirinya sendiri. Namun, saya ingin menambahkan penjelasan mengapa ini merupakan masalah karena pemahaman yang mengarahkan saya ke cara yang lebih mudah (bagi saya) untuk menghindari masalah: cukup tentukan argumen dalam panggilan alih-alih definisi.

Ini tidak bekerja:

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

tetapi ini berhasil:

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

Argumen fungsi ada di lingkungan lokal mereka sendiri.

R mencari variabel terlebih dahulu di lingkungan lokal, lalu di lingkungan global. Ini seperti bagaimana di dalam suatu fungsi suatu variabel dapat memiliki nama yang sama dengan variabel di lingkungan global, dan R akan menggunakan definisi lokal.

Memiliki definisi argumen fungsi dari lingkungan lokal mereka sendiri adalah mengapa Anda dapat memiliki nilai argumen default berdasarkan nilai argumen lainnya, seperti

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

Jadi inilah mengapa Anda tidak dapat mendefinisikan fungsi sebagai my.function <- function(x = x){}tetapi Anda dapat memanggil fungsi menggunakan my.function(x = x). Ketika Anda mendefinisikan fungsi, R menjadi bingung karena menemukan argumen x =sebagai nilai lokal x, tetapi ketika Anda memanggil fungsi R menemukan x = 4di lingkungan lokal tempat Anda menelepon.

Jadi selain memperbaiki kesalahan dengan mengubah nama argumen atau secara eksplisit menentukan lingkungan seperti yang disebutkan dalam jawaban lain, Anda juga bisa menentukan bahwa x=xketika Anda memanggil fungsi alih-alih ketika Anda mendefinisikannya. Bagi saya, menetapkan bahwa x=xdalam panggilan adalah solusi terbaik, karena tidak melibatkan sintaks tambahan atau mengakumulasi lebih banyak dan lebih banyak nama variabel.

rrr
sumber