Masalahnya, tidak ada banyak kelonggaran dalam hal fungsi encoding. Berikut adalah opsi utama:
Penulisan ulang istilah: Anda menyimpan fungsi sebagai pohon sintaksis abstraknya (atau beberapa penyandiannya. Ketika Anda memanggil suatu fungsi, Anda secara manual melintasi hierarki pohon untuk mengganti parameternya dengan argumen. Ini mudah, tetapi sangat tidak efisien dalam hal waktu dan ruang .
Penutup: Anda memiliki beberapa cara untuk mewakili suatu fungsi, mungkin pohon sintaksis, kode mesin yang lebih mungkin. Dan dalam fungsi-fungsi ini, Anda merujuk argumen Anda dengan referensi dalam beberapa cara. Ini bisa berupa pointer-offset, bisa berupa integer atau indeks De Bruijn, bisa juga sebuah nama. Kemudian Anda mewakili fungsi sebagai penutup : fungsi "instruksi" (pohon, kode, dll.) Dipasangkan dengan struktur data yang berisi semua variabel bebas fungsi. Ketika suatu fungsi benar-benar diterapkan, entah bagaimana itu tahu bagaimana mencari variabel bebas dalam struktur datanya, menggunakan lingkungan, aritmatika pointer, dll.
Saya yakin ada opsi lain, tetapi ini adalah yang mendasar, dan saya kira hampir setiap opsi lain akan menjadi varian atau optimalisasi struktur penutupan dasar.
Jadi, dalam hal kinerja, penutupan hampir secara universal berkinerja lebih baik daripada istilah penulisan ulang. Dari variasi, mana yang lebih baik? Itu sangat tergantung pada bahasa dan arsitektur Anda, tetapi saya menduga "kode mesin dengan struct yang berisi vars gratis" adalah yang paling efisien. Ia memiliki semua yang dibutuhkan fungsi (instruksi dan nilai) dan tidak lebih, dan panggilan tidak berakhir dengan melakukan traversal besar-besaran.
Saya tertarik pada kedua algoritma pengkodean saat ini menggunakan bahasa fungsional populer (Haskell, ML)
Saya bukan ahli, tapi saya 99% kebanyakan rasa ML menggunakan beberapa variasi penutupan yang saya jelaskan, meskipun dengan beberapa optimasi mungkin. Lihat ini untuk perspektif (mungkin kedaluwarsa).
Haskell melakukan sesuatu yang sedikit lebih rumit karena evaluasi malas: ia menggunakan Penulisan Ulang Grafik Tanpa Tag Tanpa Batas .
dan juga yang paling efisien yang bisa dicapai.
Apa yang paling efisien? Tidak ada implementasi yang paling efisien di semua input, sehingga Anda mendapatkan implementasi yang rata-rata efisien, tetapi masing-masing akan unggul dalam skenario yang berbeda. Jadi tidak ada peringkat pasti yang paling atau paling tidak efisien.
Tidak ada keajaiban di sini. Untuk menyimpan suatu fungsi, Anda perlu menyimpan nilai-nilai gratisnya entah bagaimana, jika tidak Anda menyandikan informasi yang lebih sedikit daripada fungsi itu sendiri. Mungkin Anda dapat mengoptimalkan beberapa nilai gratis dengan evaluasi parsial tetapi berisiko untuk kinerja, dan Anda harus berhati-hati untuk memastikan bahwa ini selalu terhenti.
Dan, mungkin Anda bisa menggunakan semacam kompresi, atau algoritma pintar untuk mendapatkan efisiensi ruang. Tetapi kemudian Anda bisa memperdagangkan waktu untuk ruang, atau Anda berada dalam situasi di mana Anda telah mengoptimalkan untuk beberapa kasus dan melambat untuk yang lain.
Anda dapat mengoptimalkan untuk kasus umum, tapi apa kasus umum adalah dapat mengubah pada bahasa, area aplikasi, dll jenis kode yang cepat untuk video game (angka-angka, loop ketat dengan masukan yang besar) mungkin berbeda dari apa yang cepat untuk kompiler (traversal pohon, daftar kerja, dll.).
Poin bonus: Apakah ada pengkodean yang memetakan fungsi bilangan bulat yang disandikan ke bilangan bulat asli (pendek, int dll dalam C). Apakah itu mungkin?
Tidak, ini tidak mungkin. Masalahnya adalah bahwa kalkulus lambda tidak membiarkan Anda melakukan introspeksi. Ketika suatu fungsi mengambil argumen dengan tipe yang sama dengan angka Gereja, ia harus dapat menyebutnya, tanpa memeriksa definisi yang tepat dari angka itu. Itulah masalahnya dengan pengkodean Gereja: satu - satunya hal yang dapat Anda lakukan dengannya adalah memanggil mereka, dan Anda dapat mensimulasikan segala sesuatu yang berguna dengan ini, tetapi bukan tanpa biaya.
Lebih penting lagi, bilangan bulat menempati setiap kemungkinan penyandian biner. Jadi, jika lambda direpresentasikan sebagai bilangan bulat mereka, Anda tidak akan memiliki cara untuk mewakili lambda non-gereja! Atau, Anda akan memperkenalkan bendera untuk menunjukkan apakah lambda adalah angka atau bukan, tetapi efisiensi apa pun yang Anda inginkan mungkin hilang.
EDIT: Sejak menulis ini, saya menyadari opsi ketiga untuk mengimplementasikan fungsi tingkat tinggi: defungsionalisasi . Di sini, setiap panggilan fungsi berubah menjadi switch
pernyataan besar , tergantung pada abstraksi lambda mana yang diberikan sebagai fungsi. Imbalannya di sini adalah bahwa ini merupakan transformasi seluruh program: Anda tidak dapat mengkompilasi bagian secara terpisah dan kemudian menghubungkannya dengan cara ini, karena Anda harus memiliki kumpulan lengkap abstraksi lambda sebelumnya.