Riffing trên câu trả lời tuyệt vời của jbapple liên quan đến replicate
, nhưng sử dụng replicateA
( replicate
được xây dựng trên) thay vào đó, tôi đã đưa ra những điều sau đây:
--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
(trong một phiên bản hơi hiệu quả hơn) đã được xác định và sử dụng trong nội tại Data.Sequence
để xây dựng cây ngón tay mà là kết quả của các loại.
Nói chung, trực giác cho replicateA
đơn giản. replicateA
được xây dựng trên đỉnh của ứng dụngTree hàm . applicativeTree
lấy một mảnh của cây có kích thước m
và tạo ra một cây cân đối có chứa các n
bản sao của cái này. Các trường hợp cho n
tối đa 8 (một Deep
ngón tay) được mã hóa cứng. Bất cứ điều gì ở trên này, và nó tự gọi đệ quy. Phần tử "ứng dụng" chỉ đơn giản là nó xen kẽ việc xây dựng cây với các hiệu ứng luồng xuyên qua, chẳng hạn như, trong trường hợp của đoạn mã trên, trạng thái.
Các go
chức năng, được nhân rộng, đơn giản chỉ là một hành động mà được tình trạng hiện tại, bật một yếu tố ra khỏi đầu, và thay thế phần còn lại. Trên mỗi lệnh gọi, do đó, bước tiếp xuống danh sách được cung cấp làm đầu vào.
Một số ghi chú cụ thể hơn
main = print (length (show (Seq.fromList [1..10000000::Int])))
Trong một số thử nghiệm đơn giản, điều này mang lại một sự đánh đổi hiệu suất thú vị. Hàm chính ở trên chạy gần như thấp hơn 1/3 với myFromList so vớifromList
. Mặt khác, myFromList
sử dụng một đống không đổi 2MB, trong khi tiêu chuẩn fromList
sử dụng lên tới 926 MB. 926 MB đó phát sinh từ việc cần phải giữ toàn bộ danh sách trong bộ nhớ cùng một lúc. Trong khi đó, giải pháp với myFromList
khả năng tiêu thụ cấu trúc theo kiểu lười biếng phát trực tuyến. Vấn đề với kết quả tốc độ từ thực tế là myFromList
phải thực hiện gấp đôi số lần phân bổ (do kết quả của việc xây dựng / phá hủy cặp đơn nguyên nhà nước) nhưfromList
. Chúng ta có thể loại bỏ những phân bổ đó bằng cách chuyển sang một đơn vị trạng thái được chuyển đổi CPS, nhưng điều đó dẫn đến việc giữ bộ nhớ nhiều hơn bất cứ lúc nào, bởi vì mất sự lười biếng đòi hỏi phải đi qua danh sách theo cách không phát trực tuyến.
Mặt khác, nếu thay vì buộc toàn bộ chuỗi bằng một chương trình, tôi chuyển sang chỉ trích xuất phần đầu hoặc phần tử cuối cùng, myFromList
ngay lập tức đưa ra một chiến thắng lớn hơn - trích xuất phần tử đầu gần như ngay lập tức và trích xuất phần tử cuối cùng là 0,8 giây . Trong khi đó, với tiêu chuẩn fromList
, trích xuất phần đầu hoặc phần tử cuối có giá ~ 2,3 giây.
Đây là tất cả các chi tiết, và là hệ quả của sự tinh khiết và lười biếng. Trong một tình huống có đột biến và truy cập ngẫu nhiên, tôi sẽ tưởng tượng replicate
giải pháp này hoàn toàn tốt hơn.
Tuy nhiên, nó đặt ra câu hỏi liệu có cách nào để viết lại applicativeTree
như vậy mà myFromList
hiệu quả hơn không. Vấn đề là, tôi nghĩ rằng các hành động áp dụng được thực hiện theo một thứ tự khác với cây được di chuyển tự nhiên, nhưng tôi chưa hoàn toàn làm việc thông qua cách thức này hoạt động, hoặc nếu có cách nào để giải quyết vấn đề này.