Selama diskusi baru-baru ini di tempat kerja, seseorang merujuk pada fungsi trampolin.
Saya telah membaca deskripsinya di Wikipedia . Ini cukup untuk memberikan gambaran umum tentang fungsinya, tetapi saya ingin sesuatu yang sedikit lebih konkret.
Apakah Anda memiliki potongan kode sederhana yang akan menggambarkan trampolin?
Jawaban:
Ada juga pengertian LISP dari 'trampolin' seperti yang dijelaskan di Wikipedia:
Misalkan kita menggunakan Javascript dan ingin menulis fungsi Fibonacci yang naif dalam gaya penerusan-lanjutan. Alasan kami melakukan ini tidak relevan - untuk mem-port Scheme ke JS misalnya, atau untuk bermain dengan CPS yang tetap harus kami gunakan untuk memanggil fungsi sisi server.
Jadi, upaya pertama adalah
Tapi, menjalankan ini dengan
n = 25
di Firefox memberikan kesalahan 'Rekursi terlalu banyak!'. Sekarang inilah sebenarnya masalah (tidak adanya optimasi panggilan-ekor dalam Javascript) yang dipecahkan oleh trampolin. Alih-alih membuat panggilan (rekursif) ke suatu fungsi, mari kitareturn
gunakan instruksi (thunk) untuk memanggil fungsi itu, untuk diinterpretasikan dalam satu putaran.sumber
Izinkan saya menambahkan beberapa contoh untuk fungsi faktorial yang diimplementasikan dengan trampolin, dalam berbagai bahasa:
Scala:
Jawa:
C (sial tanpa implementasi angka besar):
sumber
if (n < 2) Done(product)
, SO tidak mengizinkan saya untuk mengedit 1 simbol ...Saya akan memberi Anda contoh yang saya gunakan dalam patch anti-cheat untuk game online.
Saya harus bisa memindai semua file yang sedang dimuat oleh game untuk modifikasi. Jadi cara paling kuat yang saya temukan untuk melakukan ini adalah dengan menggunakan trampolin untuk CreateFileA. Jadi, ketika game diluncurkan, saya akan menemukan alamat untuk CreateFileA menggunakan GetProcAddress, lalu saya akan memodifikasi beberapa byte pertama dari fungsi tersebut dan menyisipkan kode assembly yang akan melompat ke fungsi "trampolin" saya sendiri, di mana saya akan melakukan beberapa hal, dan kemudian saya akan melompat kembali ke lokasi berikutnya di CreateFile setelah kode jmp saya. Untuk dapat melakukannya dengan andal sedikit lebih rumit dari itu, tetapi konsep dasarnya adalah hanya mengaitkan satu fungsi, memaksanya untuk mengalihkan ke fungsi lain, lalu melompat kembali ke fungsi aslinya.
Sunting: Microsoft memiliki kerangka kerja untuk hal semacam ini yang dapat Anda lihat. Disebut Memutar
sumber
Saat ini saya sedang bereksperimen dengan cara menerapkan pengoptimalan panggilan ekor untuk juru bahasa Skema, dan saat ini saya mencoba mencari tahu apakah trampolin akan layak untuk saya.
Seperti yang saya pahami, ini pada dasarnya hanyalah serangkaian panggilan fungsi yang dilakukan oleh fungsi trampolin. Setiap fungsi disebut thunk dan mengembalikan langkah berikutnya dalam komputasi hingga program berakhir (kelanjutan kosong).
Berikut adalah potongan kode pertama yang saya tulis untuk meningkatkan pemahaman saya tentang trampolin:
menghasilkan:
sumber
Berikut adalah contoh fungsi bertingkat:
compar
tidak bisa menjadi fungsi eksternal, karena menggunakannbytes
, yang hanya ada selamasort_bytes
panggilan. Pada beberapa arsitektur, fungsi rintisan kecil - trampolin - dibuat pada waktu proses, dan berisi lokasi tumpukan pemanggilan saat inisort_bytes
. Saat dipanggil, ia melompat kecompar
kode, meneruskan alamat itu.Kekacauan ini tidak diperlukan pada arsitektur seperti PowerPC, di mana ABI menentukan bahwa penunjuk fungsi sebenarnya adalah "penunjuk lemak", struktur yang berisi penunjuk ke kode yang dapat dieksekusi dan penunjuk lain ke data. Namun, pada x86, penunjuk fungsi hanyalah penunjuk.
sumber
Untuk C, trampolin akan menjadi penunjuk fungsi:
Sunting: Lebih banyak trampolin esoterik akan secara implisit dihasilkan oleh kompilator. Salah satu kegunaannya adalah meja lompat. (Meskipun ada yang jelas lebih rumit, semakin jauh Anda mulai mencoba membuat kode yang rumit.)
sumber
Sekarang C # memiliki Fungsi Lokal , pengkodean kata Game Bowling dapat diselesaikan dengan elegan dengan trampolin:
Metode
Game.RollMany
ini disebut dengan sejumlah gulungan: biasanya 20 gulungan jika tidak ada suku cadang atau pukulan.Baris pertama segera memanggil fungsi trampolin:
return Trampoline(1, 0, rs.ToList());
. Fungsi lokal ini secara rekursif melintasi larik gulungan. Fungsi lokal (trampolin) memungkinkan traversal untuk memulai dengan dua nilai tambahan: mulai denganframe
1 danrsf
(hasil sejauh ini) 0.Di dalam fungsi lokal ada operator terner yang menangani lima kasus:
Melanjutkan traversal dilakukan dengan memanggil trampolin lagi, tetapi sekarang dengan nilai yang diperbarui.
Untuk informasi lebih lanjut, cari: " tail rekursi akumulator ". Perlu diingat bahwa compiler tidak mengoptimalkan rekursi tail. Jadi, seindah solusi ini, kemungkinan besar tidak akan berpuasa.
sumber
sumber