haskell-notes

программы, они работали в интерактивном режиме, но чаще всего программы принимают какие-нибудь

начальные данные, установки или флаги. Читать начальные данные можно с помощью функций из модуля

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” fin

1: hey

2: hey

3: hey

4: 23

5: 54

6: qwe qwe qwe

7: fin

Если мы хотим, чтобы аргумент-строка содержал пробелы мы заключаем его в двойные кавычки.

С помощью функции getProgName можно узнать имя программы. Создадим программу, которая здоро-

вается при вызове. И отвечает в зависимости от настроения программы. Настроение задаётся аргументом

программы.

module Main where

import Control.Applicative

import System.Environment

main = putStrLn =<< reply getProgName getArgs

132 | Глава 8: IO

reply :: String -> [String] -> String

reply 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 happy

Hi! My name is ninja.

What a lovely day. What’s up?

*Main> :! ./ninja sad

Hi! My name is ninja.

Ooohh. Have you got some news for me?

Вызов других программ

Мы можем вызвать любую программу из нашей программы. Это делается с помощью функции system,

которая живёт в модуле System.

system :: String -> IO ExitCode

Она принимает строку и запускает её в терминале. Так же как мы делали это с помощью приставки :! в

интерпретаторе. Значение типа ExitCode говорит о результате выполнения строки. Он может быть успешным,

тогда функция вернёт ExitSuccess и закончиться ошибкой, тогда мы сможем узнать код ошибки по значению

ExitFailure Int.

Случайные значения

Функции для создания случайных значений определены в модуле System.Random. Модуль System.Random

входит в библиотеку random. Если в вашей поставке ghc его не оказалось, вы можете установить его вручную

через интернет, набрав в командной строке cabal install random. Сначала давайте разберёмся как гене-

рируются случайные числа. Стандартные случайные числа очень похожи на те, что были у нас, когда мы

рассматривали примеры специальных функций. У нас есть генератор случайных чисел типа g и с помощью

функции next мы можем получить обновлённый генератор и случайное целое число:

next :: g -> (Int, g)

Не правда ли этот тип очень похож на тип результата функций с состоянием. В качестве состояния теперь

выступает генератор случайных чисел g. Это поведение описывается классом RandomGen:

class RandomGen g where

next

:: g -> (Int, g)

split

:: g -> (g, g)

geтRange :: g -> (Int, Int)

Функция next обновляет генератор и возвращает случайное значение типа Int. Функция split раска-

лывает один генератор на два. Функция genRange возвращает диапазон значений генерируемых случайных

чисел. Первое значение в паре результата genRange должно быть всегда меньше второго. Для этого класса

определён один экземпляр, это тип StdGen. Мы можем создать первый генератор по целому числу с помощью

функции mkStdGen:

mkStdGen :: Int -> StdGen

Давайте посмотрим как это происходит в интерпретаторе:

Типичные задачи IO | 133

Prelude> :m System.Random

Prelude System.Random> let g0 = mkStdGen 0

Prelude System.Random> let (n0, g1) = next g0

Prelude System.Random> let (n1, g2) = next g1

Prelude System.Random> n0

2147482884

Prelude System.Random> n1

2092764894

Мы создали первый генератор, а затем начали получать новые. Для того, чтобы получать новые случайные

числа, нам придётся таскать везде за собой генератор случайных чисел. Мы можем обернуть его в функцию

с состоянием и пользоваться методами классов Functor, Applicative и Monad. Обновление генератора будет

происходить за ширмой, во время применения функций. Но у нас есть и другой путь.

Вместо монады State мы можем воспользоваться монадой IO. Если нам лень определять генератор слу-

чайных чисел, мы можем попросить компьютер определить его за нас. В этом случае мы взаимодействуем с

Страницы: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162