2010-02-18 2 views
8

Я пытаюсь провести небольшой эксперимент в Хаскелле, задаваясь вопросом, можно ли использовать лень для обработки ввода-вывода. Я хотел бы написать функцию, которая берет строку (список Chars) и создает строку, лениво. Я хотел бы тогда быть умело лениво подавать персонажи из IO, поэтому каждый персонаж будет обработан, как только он будет доступен, и выход будет создан, поскольку необходимые символы станут доступными. Тем не менее, я не совсем уверен, что/как я могу создать ленивый список символов из ввода внутри IO-монады."Lazy IO" в Haskell?

+0

Просьба пояснить: вы хотите использовать функцию, которая принимает строку и «производит» строку --- «производит» = «возвращает», «выходы в дескриптор файла» или что? И тогда вы хотите «лениво накормить персонажей из IO» - что это значит? – dave4420

ответ

14

Регулярная строка IO в Haskell является ленивой. Поэтому ваш пример должен просто работать из коробки.

Вот пример, используя «будут взаимодействовать» функцию, которая применяет функцию к ленивому потока символов:

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

Давайте отфильтровывать букву «е» из входного потока, лениво (т.е. пробег в постоянном пространстве):

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

Вы также можете использовать getContents и putStr, если хотите. Они все ленивы.

Запуск его для фильтрации букву «е» из словаря:

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

поэтому мы видим, что он бежал в постоянном 2M след.

+2

Вы также можете увидеть этот эффект в командной строке. Запустите программу Don 'A' в оболочке/терминале/независимо от используемой ОС и введите текст прямо в нее. Предполагая, что это строка буферизована, когда вы нажимаете клавишу ввода после каждой строки, вы сразу увидите отфильтрованный текст, даже если программа, похоже, делает только один «вызов» для фильтрации. – Nefrubyr

3
unsafeInterleaveIO :: IO a -> IO a 

unsafeInterleaveIO аллос IO вычисления, чтобы отложить лениво. Когда передано значение типа IO a, IO будет выполняться только в том случае, если требуется значение a. Это используется для чтения ленивых файлов, см. System.IO.hGetContents.

0 Например: main = getContents >>= return . map Data.Char.toUpper >>= putStr лень; когда вы загружаете символы в stdin, вы получите символы на stdout.

(Это то же самое, как написание main = interact $ map Data.Char.toUpper, как в ответ Донс гг.)

7

Самый простой способ сделать ленивый IO включает в себя такие функции, как interact, readFile, hGetContents и таким, как донов говорит; есть более подробное обсуждение этих вопросов в книге Real World Haskell, которые могут оказаться полезными. Если память обслуживает меня, все такие функции в конечном итоге реализуются с использованием unsafeInterleaveIO, что ephemient упоминает, поэтому вы можете также создавать свои собственные функции таким образом, если хотите.

С другой стороны, было бы разумно отметить, что unsafeInterleaveIO - это именно то, что он говорит на олове: небезопасный IO. Используя его - или функции на его основе - breaks purity and referential transparency. Это позволяет, по-видимому, чистые функции (т. Е. Не возвращать действие IO), чтобы влиять на внешний мир при оценке, давать разные результаты по тем же аргументам и всем этим неприятным вещам. На практике наиболее разумные способы использования unsafeInterleaveIO не вызовут проблем, и простые ошибки обычно приводят к очевидным и легко диагностированным ошибкам, но вы потеряли некоторые хорошие гарантии.

Есть альтернативы, конечно; вы можете найти различные библиотеки в Hackage, которые предоставляют ограниченные, safer lazy IO или conceptually different approaches. Однако, учитывая, что проблемы возникают редко в практическом использовании, я думаю, что большинство людей склонны придерживаться встроенных, технически небезопасных функций.