но. Например мы можем читать содержание очень большого файла и составлять какую-нибудь статистику
на основе прочитанного текста. При этом в памяти будет храниться лишь малая часть файла. Но иногда
это свойство мешает. Рассмотрим такую задачу: перевернуть текст в файле под именем ”test”. Мы должны
сначала считать текст из файла, затем перевернуть его и в конце записать в
написать эту программу так:
module Main where
main :: IO
()main =
inFile reverse ”test”inFile ::
(String -> String) -> FilePath -> IO ()inFile fun file =
writeFile file . fun =<< readFile fileТипичные задачи IO | 131
Функция inFile обновляет текст файла с помощью некоторого преобразование. Но если мы запустим эту
программу:
*Main>
main*** Exception:
test: openFile: resource busy (file is locked)Мы получили ошибку. Мы пытаемся писать в файл, который уже занят для чтения. Дело в том, что функ-
ция readFile заняла файл, за счёт чтения по кусочкам. Для решения этой проблемы необходимо воспользо-
ваться энергичной версией функции readFile, она будет читать файл целиком. Эта функция живёт в модуле
System.IO.Strict
:import qualified System.IO.Strict as
StrictIOinFile ::
(String -> String) -> FilePath -> IO ()inFile fun file =
writeFile file . fun =<< StrictIO. readFile fileФункция main осталась прежней. Теперь наша программа спокойно переворачивает текст файла.
Аргументы программы
Пока программы, которые мы создавали просили пользователя ввести данные вручную при выполнении
программы, они работали в интерактивном режиме, но чаще всего программы принимают какие-нибудь
начальные данные, установки или флаги. Читать начальные данные можно с помощью функций из модуля
System.Environment
.Узнать, что передаётся в программу можно функцией getArgs :: IO
[String]. Она возвращает списокстрок. Это те строки, что мы написали за именем программы через пробел при вызове в терминале. Напишем
простую программу, которая распечатывает свои аргументы по порядку, в виде пронумерованного списка.
module Main where
import System.Environment
main =
getArgs >>= mapM_ putStrLn . zipWith f [1 .. ]where
f n a = show n ++ ”: ” ++ aВ локальной функции f мы присоединяем к строке номер через двоеточие. Функцией mapM_ мы пробегаем
по списку строк, отображая их с помощью функции putStrLn. Обратите внимание на краткость программы,
с помощью функции композиции мы легко составили функцию, которая приписывает к аргументам числа, а
затем выводит их на экран.
Скомпилируем программу в интерпретаторе и вызовем её.
*Main> :!
ghc --make Args[1 of
1] Compiling Main( Args.
hs, Args. o )Linking Args ...
*Main> :! ./Args
hey hey hey 23 54 ”qwe qwe qwe” fin1:
hey2:
hey3:
hey4:
235:
546:
qwe qwe qwe7:
finЕсли мы хотим, чтобы аргумент-строка содержал пробелы мы заключаем его в двойные кавычки.
С помощью функции getProgName можно узнать имя программы. Создадим программу, которая здоро-
вается при вызове. И отвечает в зависимости от настроения программы. Настроение задаётся аргументом
программы.
module Main where
import Control.Applicative
import System.Environment
main =
putStrLn =<< reply <$> getProgName <*> getArgs132 | Глава 8: IO
reply :: String ->
[String] -> Stringreply name (x:_
) = hi name ++ case x of”happy”
->
”What a lovely day. What’s up?””sad”
->
”Ooohh. Have you got some news for me?””neutral”
->
”How are you?”reply name _
=
reply name [”neutral”]hi :: String -> String
hi name =
”Hi! My name is ” ++ name ++ ”.\n”В функции reply мы составляем реплику программы. Она зависит от имени программы и поступающих
на вход аргументов. Посмотрим, что у нас получилось:
*Main> :!
ghc --make HowAreYou.hs -o ninja[1 of
1] Compiling Main( HowAreYou.
hs, HowAreYou. o )Linking
ninja ...*Main> :! ./
ninja happyHi! My
name is ninja.What
a lovely day. What’s up?*Main> :! ./
ninja sadHi! My
name is ninja.Ooohh. Have
you got some news for me?Вызов других программ
Мы можем вызвать любую программу из нашей программы. Это делается с помощью функции system,
которая живёт в модуле System
.system :: String -> IO ExitCode
Она принимает строку и запускает её в терминале. Так же как мы делали это с помощью приставки :!
винтерпретаторе. Значение типа ExitCode
говорит о результате выполнения строки. Он может быть успешным,тогда функция вернёт ExitSuccess
и закончиться ошибкой, тогда мы сможем узнать код ошибки по значениюExitFailure Int
.Случайные значения