Katakanlah saya memiliki fungsi rekursif dasar:
function recur(data) {
data = data+1;
var nothing = function() {
recur(data);
}
nothing();
}
Bagaimana saya bisa melakukan ini jika saya memiliki fungsi anonim seperti ...
(function(data){
data = data+1;
var nothing = function() {
//Something here that calls the function?
}
nothing();
})();
Saya ingin cara memanggil fungsi yang memanggil fungsi ini ... Saya telah melihat skrip di suatu tempat (saya tidak ingat di mana) yang dapat memberi tahu Anda nama fungsi yang dipanggil, tetapi saya tidak dapat mengingat satu pun informasi itu sekarang.
javascript
recursion
scope
anonymous-function
Penyamaran
sumber
sumber
arguments.callee
ada, dan functnio ini tidak melakukan sesuatu yang berguna. Aku melihat ke atas Y Combinator:P
. Sial, hal itu tidak akan pernah berguna ...Jawaban:
Anda bisa memberi nama fungsi, bahkan saat Anda membuat fungsi sebagai nilai dan bukan pernyataan "deklarasi fungsi". Dengan kata lain:
adalah fungsi rekursif stack-blowing. Sekarang, yang mengatakan, Anda
mungkin tidakmungkin tidak ingin melakukan ini secara umum karena ada beberapa masalah aneh dengan berbagai implementasi Javascript. ( catatan - itu adalah komentar yang cukup lama; beberapa / banyak / semua masalah yang dijelaskan dalam posting blog Kangax mungkin diperbaiki di browser yang lebih modern.)Ketika Anda memberi nama seperti itu, nama itu tidak terlihat di luar fungsi (yah, itu tidak seharusnya; itu salah satu keanehan). Ini seperti "letrec" di Lisp.
Adapun
arguments.callee
, hal itu tidak diizinkan dalam mode "ketat" dan umumnya dianggap sebagai hal yang buruk, karena akan mempersulit pengoptimalan. Ini juga jauh lebih lambat dari yang diperkirakan.edit - Jika Anda ingin mendapatkan efek dari fungsi "anonim" yang dapat memanggil dirinya sendiri, Anda dapat melakukan sesuatu seperti ini (dengan asumsi Anda meneruskan fungsi tersebut sebagai panggilan balik atau sesuatu seperti itu):
Apa yang dilakukannya adalah menentukan fungsi dengan pernyataan deklarasi fungsi yang bagus, aman, tidak rusak di IE , membuat fungsi lokal yang namanya tidak akan mencemari namespace global. Fungsi wrapper (benar-benar anonim) hanya mengembalikan fungsi lokal tersebut.
sumber
(() => { call_recursively_self_here() })()
dan memanggil dirinya sendiri secara rekursif, bukan? Saya harus memberinya nama.Orang-orang membicarakan tentang kombinator Y dalam komentar, tetapi tidak ada yang menulisnya sebagai jawaban.
Kombinator Y dapat didefinisikan dalam javascript sebagai berikut: (terima kasih kepada steamer25 untuk tautannya)
Dan ketika Anda ingin meneruskan fungsi anonim Anda:
Hal terpenting yang perlu diperhatikan tentang solusi ini adalah Anda tidak boleh menggunakannya.
sumber
U kombinator
Dengan meneruskan fungsi ke dirinya sendiri sebagai argumen, sebuah fungsi bisa berulang menggunakan parameternya alih-alih namanya! Jadi fungsi yang diberikan
U
harus memiliki setidaknya satu parameter yang akan mengikat fungsi (itu sendiri).Dalam contoh di bawah ini, kami tidak memiliki kondisi keluar, jadi kami hanya akan mengulang tanpa batas hingga tumpukan overflow terjadi
Kami dapat menghentikan rekursi tak terbatas menggunakan berbagai teknik. Di sini, saya akan menulis fungsi anonim kami untuk mengembalikan fungsi anonim lain yang menunggu masukan; dalam hal ini, beberapa nomor. Ketika angka diberikan, jika lebih besar dari 0, kami akan terus berulang, jika tidak mengembalikan 0.
Apa yang tidak langsung terlihat di sini adalah bahwa fungsi kita, saat pertama kali diterapkan ke dirinya sendiri menggunakan
U
kombinator, ia mengembalikan fungsi yang menunggu masukan pertama. Jika kami memberi nama untuk ini, dapat secara efektif membangun fungsi rekursif menggunakan lambda (fungsi anonim)Hanya ini bukan rekursi langsung - fungsi yang memanggil dirinya sendiri menggunakan namanya sendiri. Definisi kami tentang
countDown
tidak merujuk dirinya sendiri di dalam tubuhnya dan rekursi tetap dimungkinkanCara menghapus referensi mandiri dari fungsi yang ada menggunakan kombinator U.
Di sini saya akan menunjukkan kepada Anda cara mengambil fungsi rekursif yang menggunakan referensi ke dirinya sendiri dan mengubahnya menjadi fungsi yang menggunakan kombinator U sebagai pengganti referensi mandiri.
Sekarang menggunakan kombinator U untuk mengganti referensi bagian dalam
factorial
Pola penggantian dasarnya adalah ini. Buat catatan mental, kami akan menggunakan strategi serupa di bagian selanjutnya
Kombinator Y
Pada bagian sebelumnya kita telah melihat bagaimana mengubah rekursi referensi diri menjadi fungsi rekursif yang tidak bergantung pada fungsi bernama menggunakan kombinator U. Ada sedikit gangguan karena harus ingat untuk selalu meneruskan fungsi ke dirinya sendiri sebagai argumen pertama. Nah, kombinator-Y dibangun di atas kombinator-U dan menghilangkan bagian yang membosankan itu. Ini adalah hal yang baik karena menghilangkan / mengurangi kompleksitas adalah alasan utama kami membuat fungsi
Pertama, mari turunkan kombinator-Y kita sendiri
Sekarang kita akan melihat bagaimana penggunaannya dibandingkan dengan U-combinator. Perhatikan, untuk berulang, alih-alih
U (f)
kita bisa langsung meneleponf ()
Sekarang saya akan mendemonstrasikan penggunaan
countDown
programY
- Anda akan melihat programnya hampir identik tetapi kombinator Y menjaga semuanya sedikit lebih bersihDan sekarang kita akan lihat
factorial
jugaSeperti yang Anda lihat,
f
menjadi mekanisme rekursi itu sendiri. Untuk berulang, kami menyebutnya seperti fungsi biasa. Kita bisa memanggilnya berkali-kali dengan argumen berbeda dan hasilnya akan tetap benar. Dan karena ini adalah parameter fungsi biasa, kita dapat menamainya sesuka kita, seperti direcur
bawah ini -Kombinator U dan Y dengan lebih dari 1 parameter
Pada contoh di atas, kita melihat bagaimana kita dapat mengulang dan meneruskan argumen untuk melacak "status" dari komputasi kita. Tetapi bagaimana jika kita perlu melacak status tambahan?
Kita bisa menggunakan data gabungan seperti Array atau sesuatu ...
Tapi ini buruk karena mengekspos keadaan internal (penghitung
a
danb
). Alangkah baiknya jika kita bisa meneleponfibonacci (7)
untuk mendapatkan jawaban yang kita inginkan.Menggunakan apa yang kita ketahui tentang fungsi curried (urutan fungsi unary (1-paramter)), kita dapat mencapai tujuan kita dengan mudah tanpa harus mengubah definisi kita
Y
atau mengandalkan data gabungan atau fitur bahasa tingkat lanjut.Perhatikan definisi di
fibonacci
bawah ini. Kami segera melamar0
dan1
yang terikata
danb
masing - masing. Sekarang fibonacci hanya menunggu argumen terakhir yang akan diberikanx
. Ketika kita berulang, kita harus memanggilf (a) (b) (x)
(bukanf (a,b,x)
) karena fungsi kita dalam bentuk kari.Pola semacam ini dapat berguna untuk mendefinisikan semua jenis fungsi. Di bawah ini kita akan melihat dua fungsi yang lebih didefinisikan dengan menggunakan
Y
combinator (range
danreduce
) dan turunan darireduce
,map
.INI SEMUA ANONIM OMG
Karena kami bekerja dengan fungsi murni di sini, kami dapat mengganti fungsi bernama apa pun untuk definisinya. Perhatikan apa yang terjadi saat kita menggunakan fibonacci dan mengganti fungsi bernama dengan ekspresinya
Dan begitulah -
fibonacci (7)
dihitung secara rekursif hanya dengan menggunakan fungsi anonimsumber
Mungkin paling sederhana menggunakan "objek anonim" sebagai gantinya:
Ruang global Anda benar-benar tidak tercemar. Ini sangat mudah. Dan Anda dapat dengan mudah memanfaatkan status non-global objek.
Anda juga dapat menggunakan metode objek ES6 untuk membuat sintaks lebih ringkas.
sumber
Saya tidak akan melakukan ini sebagai fungsi sebaris. Itu mendorong batas-batas selera yang baik dan tidak benar-benar memberi Anda apa pun.
Jika memang harus, ada
arguments.callee
seperti dalam jawaban Fabrizio. Namun hal ini umumnya dianggap tidak disarankan dan tidak diizinkan dalam 'mode ketat' ECMAScript Fifth Edition. Meskipun ECMA 3 dan mode non-ketat tidak akan hilang, bekerja dalam mode ketat menjanjikan lebih banyak kemungkinan pengoptimalan bahasa.Seseorang juga dapat menggunakan fungsi sebaris bernama:
Namun, ekspresi fungsi sebaris bernama juga sebaiknya dihindari, karena JScript IE melakukan beberapa hal buruk padanya. Dalam contoh di atas
foo
salah mencemari lingkup induk di IE, dan indukfoo
adalah contoh terpisah untukfoo
terlihat di dalamfoo
.Apa tujuan meletakkan ini dalam fungsi anonim sebaris? Jika Anda hanya ingin menghindari mencemari lingkup induk, tentu saja Anda dapat menyembunyikan contoh pertama Anda di dalam fungsi anonim (namespace) panggilan mandiri lainnya. Apakah Anda benar-benar perlu membuat salinan baru
nothing
setiap kali sekitar rekursi? Anda mungkin lebih baik menggunakan namespace yang berisi dua fungsi sederhana yang saling rekursif.sumber
"pushing against the boundaries of good taste"
- (baik, dan info bagus).recur_foo
akan bertabrakan dengan fungsi di lingkup induk (atau menjadi sakit -digunakan).sumber
arguments.callee
: itu tidak diizinkan dalam mode ketat dan di ES5.Anda bisa melakukan sesuatu seperti:
atau dalam kasus Anda:
sumber
recur
terlebih dahulu denganvar
pernyataan. Entah apakah itu melanggar aturan pertanyaan, tetapi seperti yang Anda miliki sekarang, tanpavar
pernyataan Anda akan mendapatkan kesalahan dalam mode ketat ECMAScript 5.var
kata kunci, tetapi begitu saya menguji kode ini, kode ini menimbulkan kesalahan, karena Anda tidak dapat benar-benar mendeklarasikan variabel di dalam blok pemanggilan sendiri, dan pendekatan saya bergantung pada deklarasi otomatis dari variabel yang tidak ditentukan, dan karenanya @ Pointy's solusinya lebih tepat. Tapi saya masih memilih jawaban Fabrizio Calderan;)(var recur = function() {...})();
tidak akan berfungsi karena sekarang ini adalah pernyataan daripada ekspresi tugas (yang mengembalikan nilai yang ditetapkan). Sayavar recur; (recur = function() {...})();
malah menyarankan .Saat Anda mendeklarasikan fungsi anonim seperti ini:
Ini dianggap sebagai ekspresi fungsi dan memiliki nama opsional (yang dapat Anda gunakan untuk memanggilnya dari dalam dirinya sendiri. Tetapi karena ini adalah ekspresi fungsi (dan bukan pernyataan), ia tetap anonim (tetapi memiliki nama yang dapat Anda panggil). Jadi fungsi ini dapat memanggil dirinya sendiri:
sumber
foo
tidak dideklarasikan dalam konteks saat ini, tetapi itu kurang lebih tidak relevan. Fungsi dengan nama masih merupakan fungsi bernama - bukan anonim.Mengapa tidak meneruskan fungsi ke functio itu sendiri?
sumber
Dalam situasi tertentu Anda harus mengandalkan fungsi anonim. Diberikan adalah
map
fungsi rekursif :Harap dicatat bahwa
map
tidak boleh mengubah struktur array. Jadi akumulatoracc
tidak perlu diekspos. Kita bisa membungkusmap
fungsi lain misalnya:Tapi solusi ini cukup bertele-tele. Mari gunakan
U
kombinator yang diremehkan :Ringkas, bukan?
U
sangat sederhana tetapi memiliki kelemahan bahwa panggilan rekursif menjadi agak dikaburkan:sum(...)
menjadih(h)(...)
- itu saja.sumber
Saya tidak yakin apakah jawabannya masih diperlukan tetapi ini juga dapat dilakukan dengan menggunakan delegasi yang dibuat menggunakan function.bind:
Ini tidak melibatkan fungsi atau argumen bernama.callee.
sumber
Seperti bobince menulis, cukup beri nama fungsi Anda.
Tapi, saya rasa Anda juga ingin meneruskan nilai awal dan akhirnya menghentikan fungsi Anda!
Contoh jsFiddle yang berfungsi (menggunakan data + = data untuk bersenang-senang)
sumber
However named inline function expressions are also best avoided.
. Tapi OP juga melenceng ... :)saya membutuhkan (atau lebih tepatnya, menginginkan) fungsi anonim satu baris untuk berjalan di atas objek yang membangun string, dan menanganinya seperti ini:
yang menghasilkan string seperti 'Root: foo: bar: baz: ...'
sumber
Dengan ES2015 kita bisa bermain-main sedikit dengan sintaks dan penyalahgunaan parameter default dan thunks. Yang terakhir hanyalah fungsi tanpa argumen:
Harap dicatat bahwa
f
ini adalah parameter dengan fungsi anonim(x, y, n) => n === 0 ? x : f(y, x + y, n - 1)
sebagai nilai defaultnya. Kapanf
dipanggil olehapplyT
pemanggilan ini harus dilakukan tanpa argumen, sehingga nilai default yang digunakan. Nilai default-nya adalah sebuah fungsi dan karenanyaf
merupakan fungsi bernama, yang dapat memanggil dirinya sendiri secara rekursif.sumber
Jawaban lain yang tidak melibatkan fungsi bernama atau argumen.callee
sumber
Ini adalah pengerjaan ulang jawaban jforjs dengan nama berbeda dan entri yang sedikit dimodifikasi.
Tidak perlu membuka gulungan rekursi pertama. Fungsi yang menerima dirinya sendiri sebagai referensi mengingatkan kembali pada cairan primordial OOP.
sumber
Ini adalah versi jawaban @ zem dengan fungsi panah.
Anda dapat menggunakan
U
atauY
kombinator. Kombinator Y menjadi yang paling sederhana untuk digunakan.U
combinator, dengan ini Anda harus terus meneruskan fungsinya:const U = f => f(f) U(selfFn => arg => selfFn(selfFn)('to infinity and beyond'))
Y
combinator, dengan ini Anda tidak harus terus meneruskan fungsi:const Y = gen => U(f => gen((...args) => f(f)(...args))) Y(selfFn => arg => selfFn('to infinity and beyond'))
sumber
Namun solusi Y-combinator lainnya, menggunakan tautan kode rosetta (saya pikir seseorang sebelumnya menyebutkan tautan di suatu tempat di stackOverflow.
Panah adalah untuk fungsi anonim yang lebih mudah dibaca oleh saya:
sumber
Ini mungkin tidak berfungsi di semua tempat, tetapi Anda dapat menggunakan
arguments.callee
untuk merujuk ke fungsi saat ini.Jadi, faktorial dapat dilakukan sebagai berikut:
sumber