Mengetahui jawaban jbapple yang sangat baik tentang replicate, tetapi menggunakan replicateA(yang replicatedibangun di atas), saya datang dengan yang berikut:
--Unlike fromList, one needs the length explicitly.
myFromList :: Int -> [b] -> Seq b
myFromList l xs = flip evalState xs $ Seq.replicateA l go
where go = do
(y:ys) <- get
put ys
return y
myFromList(dalam versi yang sedikit lebih efisien) sudah didefinisikan dan digunakan secara internal dalam Data.Sequenceuntuk membangun pohon jari yang hasil macam.
Secara umum, intuisi untuk replicateAsederhana. replicateAdibangun di atas fungsi applicativeTree . applicativeTreemengambil sepotong pohon ukuran m, dan menghasilkan pohon seimbang yang mengandung nsalinan ini. Kasing nhingga 8 (satu Deepjari) dikodekan dengan keras. Apa pun di atas ini, dan itu memanggil dirinya sendiri secara rekursif. Unsur "aplikatif" hanyalah bahwa ia menyisipkan konstruksi pohon dengan efek threading melalui, seperti, dalam kasus kode di atas, menyatakan.
The gofungsi, yang direplikasi, hanyalah sebuah tindakan yang mendapat kondisi saat ini, muncul sebuah elemen dari atas, dan menggantikan sisanya. Pada setiap doa, itu dengan demikian melangkah lebih jauh ke bawah daftar yang disediakan sebagai input.
Beberapa catatan lebih konkret
main = print (length (show (Seq.fromList [1..10000000::Int])))
Pada beberapa tes sederhana, ini menghasilkan tradeoff kinerja yang menarik. Fungsi utama di atas berjalan hampir 1/3 lebih rendah dengan myFromList daripada dengan fromList. Di sisi lain, myFromListdigunakan tumpukan konstan 2MB, sedangkan standar fromListdigunakan hingga 926MB. 926MB itu muncul dari keharusan untuk menahan seluruh daftar di memori sekaligus. Sementara itu, solusi dengan myFromListmampu mengkonsumsi struktur dengan cara streaming malas. Masalah dengan kecepatan hasil dari fakta yang myFromListharus melakukan alokasi kira-kira dua kali lebih banyak (sebagai akibat dari pembangunan pasangan / penghancuran monad negara) sebagaifromList. Kita dapat menghilangkan alokasi tersebut dengan pindah ke state monad yang diubah CPS, tetapi hal itu menghasilkan memori yang jauh lebih banyak pada waktu tertentu, karena kehilangan kemalasan mengharuskan untuk melintasi daftar secara non-streaming.
Di sisi lain, jika alih-alih memaksa seluruh urutan dengan sebuah pertunjukan, saya pindah ke hanya mengekstraksi kepala atau elemen terakhir, myFromListsegera menyajikan kemenangan yang lebih besar - mengekstraksi elemen kepala hampir instan, dan mengekstraksi elemen terakhir adalah 0,8 detik. . Sementara itu, dengan standar fromList, mengekstraksi kepala atau elemen terakhir membutuhkan biaya ~ 2,3 detik.
Ini semua detail, dan merupakan konsekuensi dari kemurnian dan kemalasan. Dalam situasi dengan mutasi dan akses acak, saya akan membayangkan replicatesolusinya benar-benar lebih baik.
Namun, hal itu menimbulkan pertanyaan apakah ada cara untuk menulis ulang applicativeTreesedemikian rupa sehingga myFromListlebih efisien. Masalahnya adalah, saya pikir, bahwa tindakan aplikatif dijalankan dalam urutan yang berbeda dari pohon secara alami dilalui, tetapi saya belum sepenuhnya bekerja melalui bagaimana ini bekerja, atau jika ada cara untuk menyelesaikan ini.