Berkat evaluasi malas, program Haskell tidak (hampir tidak bisa ) melakukan apa yang terlihat seperti yang dilakukannya.
Pertimbangkan program ini:
main = putStrLn (show (quicksort [8, 6, 7, 5, 3, 0, 9]))
Dalam bahasa yang bersemangat, pertama quicksort
akan lari, lalu show
, lalu putStrLn
. Argumen fungsi dihitung sebelum fungsi tersebut mulai berjalan.
Di Haskell, justru sebaliknya. Fungsi tersebut mulai berjalan lebih dulu. Argumen hanya dihitung saat fungsi benar-benar menggunakannya. Dan argumen gabungan, seperti daftar, dihitung satu per satu, karena setiap bagian digunakan.
Jadi hal pertama yang terjadi dalam program ini adalah yang putStrLn
mulai berjalan.
Implementasi GHC dari putStrLn
bekerja dengan menyalin karakter dari argumen String ke dalam buffer keluaran. Tapi saat memasuki loop ini, show
belum berjalan. Oleh karena itu, saat akan menyalin karakter pertama dari string, Haskell mengevaluasi pecahan dari panggilan show
dan yang quicksort
diperlukan untuk menghitung karakter itu . Kemudian putStrLn
beralih ke karakter berikutnya. Jadi eksekusi ketiga functions- putStrLn
, show
dan quicksort
- disisipkan. quicksort
dieksekusi secara bertahap, meninggalkan grafik halangan yang tidak dievaluasi untuk mengingat di mana ia tinggalkan.
Sekarang ini sangat berbeda dari apa yang mungkin Anda harapkan jika Anda terbiasa dengan, Anda tahu, bahasa pemrograman lain yang pernah ada. Tidak mudah untuk memvisualisasikan bagaimana quicksort
sebenarnya berperilaku di Haskell dalam hal akses memori atau bahkan urutan perbandingan. Jika Anda hanya dapat mengamati perilakunya, dan bukan kode sumbernya, Anda tidak akan mengenali apa yang dilakukannya sebagai quicksort .
Misalnya, versi C dari quicksort mempartisi semua data sebelum panggilan rekursif pertama. Dalam versi Haskell, elemen pertama dari hasil akan dihitung (dan bahkan dapat muncul di layar Anda) sebelum partisi pertama selesai dijalankan — bahkan sebelum pekerjaan apa pun diselesaikan greater
.
PS Kode Haskell akan lebih seperti quicksort jika ia melakukan jumlah perbandingan yang sama dengan quicksort; kode seperti yang tertulis melakukan perbandingan dua kali lebih banyak karena lesser
dan greater
ditentukan untuk dihitung secara independen, melakukan dua pemindaian linier melalui daftar. Tentu saja pada prinsipnya mungkin bagi compiler untuk menjadi cukup pintar untuk menghilangkan perbandingan ekstra; atau kode dapat diubah untuk digunakan Data.List.partition
.
PPS Contoh klasik dari algoritma Haskell ternyata tidak berperilaku seperti yang Anda harapkan adalah saringan Eratosthenes untuk menghitung bilangan prima.