Difference between revisions of "Ru/IO"
(Первая версия) |
(+IO_inside link) |
||
Line 77: | Line 77: | ||
Здесь "Hi" печатается не в момент вызова example (для него операция печати - это всего лишь пассивный параметр), а в тот момент, когда вызов action вставлен в do-последовательность |
Здесь "Hi" печатается не в момент вызова example (для него операция печати - это всего лишь пассивный параметр), а в тот момент, когда вызов action вставлен в do-последовательность |
||
+ | |||
+ | |||
+ | Хотите знать больше? :) Читайте http://haskell.org/haskellwiki/IO_inside |
Revision as of 09:42, 10 September 2007
В императивных языках вроде C++ нет разделения функций на чистые и имеющие побочные эффекты, любая функция рассматривается как потенциально "грязная". С одной стороны, это облегчает модификацию программы (любая чисто вычислительная функция может быть переделана в имеющую побочные эффекты), с другой стороны - усложняет понимание программы, её отладку и модификацию - какая-нибудь скромная функция sin может иметь совершенно нескромные побочные эффекты ;)
В отличие от них, в Haskell все функции чётко поделены на два класса, и чистые функции не могут вызыыать нечистые. Для удобства дальнейшего изложения давайте условимся называть чистые функции просто функциями, а нечистые - процедурами. Итак, функция - это просто способ вычисления выходного значения по входным, а процедура выполняет некоторое действие (и может иметь выходное значение).
Вычисления внутри функций производятся по мере необходимости и в том порядке, в каком в них возникает необходимость. В отличие от этого процедура описывает последовательность операций, которые выполняются обязательно и обязательно в указанном порядке. Поэтому способ записи, применяемый для определения функций, не годится для процедур, и для них используется специальная do-нотация, сходная с императивными языками
Главная выполняемая функция в хаскеле - main - является процедурой, и на ней мы рассмотрим примеры описания процедур:
Простейшая процедура, выполняющая два действия. Как мы уже говорили, они выполняются строго в заданном порядке:
main = do print "Hello, World!"
print "Haskell rules, C++ sucks!"
"Действия", выполнямые в do - это в свою очередь вызовы других процедур. Мы можем присваивать "переменным" значения, возвращаемые из этих процедур, и организовывать условное выполнение:
main = do print "Hey, kid, what is your name?"
name <- getLine
if name=="Bulat"
then do print "Thank you, Creator"
else do print ("Hi, "++name)
Аналогичным образом можно применять case:
...
Для организации циклов, как обычно, используется рекурсия. Возвратить значение из порцедуры можно с помощью "return". Опишем процедуру, которая вводит непустую строку:
myGetLine = do str <- getLine
if str==""
then do print "Пожалуйств, введите непустую строку"
myGetLine
else return str
и пример её применения:
main = do print "Hey, kid, what is your name?"
name <- myGetLine
if name=="Bulat"
then do print "Thank you, Creator"
else do print ("Hi, "++name)
Совершенно аналогично функциям, процедуры могут иметь входные параметры. Для описания чистых вычислений внутри процедур можно использовать let-блоки, однако вычисления в let-блоке не могут ссылаться на имена, определённые в нижеследующих действиях или let-блоках:
math x y = do
let x2 = x*2
x3 = x2*x
xy = x*y
print ("x=" ++ (show x))
print ("x в квадрате=" ++ (show x2))
print ("x в кубе=" ++ (show x3))
print ("произведение x и y=" ++ (show xy))
main = do math 2 2
math 3 4
А теперь забацаем пример, который включает все вышеприведённые извраты:
...
Наконец, давайте вернёмся к истокам и вспомним, что "процедуры" в хаскеле - это всего лишь грязные функции, которые могут иметь побочные эффекты, а функции в хаскеле являются "первоклассными" значениями. Это значит, что процедуры, как и любые другие функции, можно передавать в качестве параметров, сохранять в структурах данных, "добивать" параметрами. Различие всего одно - функция, применённая ко всем своим параметрами, является уже значением - это значение может храниться невычисленным только благодаря lazy evaluation. Процедура же, даже со всеми своими параметрами, остаётся процедурой (или если хотите действием) и выполняется ровно в тот момент, когда она вызвано в do-нотации. Пример:
main = do example (print "Hi")
example action = do print "Before"
action
print "After"
Здесь "Hi" печатается не в момент вызова example (для него операция печати - это всего лишь пассивный параметр), а в тот момент, когда вызов action вставлен в do-последовательность
Хотите знать больше? :) Читайте http://haskell.org/haskellwiki/IO_inside