2010-02-18 35 views
8

Tôi đang thử một chút thử nghiệm trong haskell, tự hỏi liệu có thể khai thác sự lười biếng để xử lý IO hay không. Tôi muốn viết một hàm nhận một String (một danh sách các ký tự) và tạo ra một chuỗi, một cách lười biếng. Sau đó tôi muốn được lười biếng để nuôi dưỡng các nhân vật từ IO, do đó, mỗi nhân vật sẽ được xử lý ngay sau khi nó có sẵn, và đầu ra sẽ được tạo ra khi các ký tự cần thiết có sẵn. Tuy nhiên, tôi không hoàn toàn chắc chắn nếu/làm thế nào tôi có thể tạo ra một danh sách lười biếng của các ký tự từ đầu vào bên trong đơn nguyên IO."Lazy IO" trong Haskell?

+0

Hãy làm rõ: bạn muốn một hàm nhận chuỗi và "tạo" chuỗi --- "tạo" = "trả về", "kết quả đầu ra cho xử lý tệp" hoặc cái gì? Và sau đó bạn muốn "lười biếng ăn các nhân vật từ IO" --- điều này có nghĩa là gì? – dave4420

Trả lời

14

Chuỗi thông thường IO trong Haskell rất lười. Vì vậy, ví dụ của bạn chỉ nên làm việc ra khỏi hộp.

Dưới đây là một ví dụ, bằng cách sử dụng chức năng 'tương tác', mà áp dụng một chức năng để một dòng suối lười biếng của các nhân vật:

interact :: (String -> String) -> IO() 

Hãy lọc ra chữ 'e' từ input stream, uể oải (tức là chạy trong không gian cố định):

main = interact $ filter (/= 'e') 

Bạn cũng có thể sử dụng getContents và putStr nếu muốn. Tất cả họ đều lười biếng.

Chạy nó để lọc chữ 'e' khỏi từ điển:

$ ghc -O2 --make A.hs 
$ ./A +RTS -s < /usr/share/dict/words 
... 
       2 MB total memory in use (0 MB lost due to fragmentation) 
... 

vì vậy chúng tôi thấy rằng nó chạy trong một diện tích 2M liên tục.

+2

Bạn cũng có thể thấy hiệu ứng này tại dòng lệnh. Chạy chương trình 'A' của Don trong shell/Terminal/bất kỳ hệ điều hành nào của bạn sử dụng và nhập văn bản thẳng vào nó. Giả sử dòng đệm của nó, khi bạn nhấn enter sau mỗi dòng bạn sẽ thấy văn bản được lọc được in ngay lập tức, mặc dù chương trình dường như chỉ thực hiện một "cuộc gọi" tới 'bộ lọc'. – Nefrubyr

3
unsafeInterleaveIO :: IO a -> IO a 

unsafeInterleaveIO allos IO tính toán để được hoãn lại uể oải. Khi vượt qua giá trị loại IO a, IO sẽ chỉ được thực hiện khi giá trị của a được yêu cầu. Điều này được sử dụng để thực hiện đọc tập tin lười biếng, xem System.IO.hGetContents.

Ví dụ: main = getContents >>= return . map Data.Char.toUpper >>= putStr là lười; khi bạn nạp các ký tự vào stdin, bạn sẽ nhận được các ký tự trên stdout.

(Điều này cũng giống như viết main = interact $ map Data.Char.toUpper, như trong câu trả lời Dons của.)

7

Phương pháp đơn giản nhất của việc lười biếng IO liên quan đến các chức năng như interact, readFile, hGetContents, và như vậy, như Dons nói; có một cuộc thảo luận mở rộng hơn về những điều này trong cuốn sách Real World Haskell mà bạn có thể thấy hữu ích. Nếu bộ nhớ phục vụ cho tôi, tất cả các chức năng như vậy cuối cùng sẽ được triển khai bằng cách sử dụng unsafeInterleaveIO rằng ephemient các đề cập, vì vậy bạn cũng có thể xây dựng các chức năng của riêng mình theo cách bạn muốn.

Mặt khác, có thể cần lưu ý rằng unsafeInterleaveIO chính xác là những gì nó nói trên tin: không an toàn IO. Sử dụng nó - hoặc các chức năng dựa trên nó - breaks purity and referential transparency. Điều này cho phép các hàm thuần túy rõ ràng (nghĩa là, không trả về hành động IO) để thực hiện thế giới bên ngoài khi được đánh giá, tạo ra các kết quả khác nhau từ cùng một đối số và tất cả những thứ khó chịu khác. Trong thực tế, hầu hết các cách hợp lý để sử dụng unsafeInterleaveIO sẽ không gây ra vấn đề gì, và các lỗi đơn giản thường sẽ dẫn đến lỗi được chẩn đoán rõ ràng và dễ dàng, nhưng bạn đã mất một số bảo đảm tốt đẹp.

Có các lựa chọn thay thế, tất nhiên; bạn có thể tìm thấy các loại thư viện trên Hackage cung cấp bị hạn chế, safer lazy IO hoặc conceptually different approaches. Tuy nhiên, do các vấn đề phát sinh hiếm khi được sử dụng trong thực tế, tôi nghĩ hầu hết mọi người có xu hướng gắn bó với các chức năng không an toàn được tích hợp sẵn.