Difference between revisions of "Ru/IO"
(dobavil 2 ssylki na rus) |
m (Убрана ссылка на статью о монадах) |
||
Line 119: | Line 119: | ||
== Ссылки == |
== Ссылки == |
||
* (''на русском'') |
* (''на русском'') |
||
− | ** [http://rsdn.ru/article/haskell/haskell_part2.xml#E1JAC Мягкое введение в Haskell. Часть 9: О Монадах] |
||
** [http://ru.wikibooks.org/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F/%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B8_%D0%B2%D0%B2%D0%BE%D0%B4%D0%B0/%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4%D0%B0_%D0%B2_Haskell'%D0%B5 Основы функционального программирования/Операции ввода/вывода в Haskell'е] |
** [http://ru.wikibooks.org/wiki/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B_%D1%84%D1%83%D0%BD%D0%BA%D1%86%D0%B8%D0%BE%D0%BD%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B3%D0%BE_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D1%8F/%D0%9E%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B8_%D0%B2%D0%B2%D0%BE%D0%B4%D0%B0/%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4%D0%B0_%D0%B2_Haskell'%D0%B5 Основы функционального программирования/Операции ввода/вывода в Haskell'е] |
||
* (''english'') |
* (''english'') |
Revision as of 12:48, 19 September 2007
Функции и процедуры
В императивных языках вроде C++ нет разделения функций на чистые и имеющие побочные эффекты, любая функция рассматривается как потенциально "грязная". С одной стороны, это облегчает модификацию программы (к любой чисто вычислительной функции могут быть добавлены побочные эффекты), с другой стороны - усложняет понимание программы, её отладку и модификацию. Какая-нибудь скромная функция sin может иметь совершенно нескромные побочные эффекты, например стереть системные файлы.
В отличие от них, в Haskell все функции чётко поделены на два класса. Для удобства дальнейшего изложения давайте условимся называть чистые функции просто функциями, а нечистые - процедурами. Итак, функция - это просто однозначный способ вычисления выходного значения по входным, а процедура выполняет некоторое действие (хотя может иметь и выходное значение).
Вычисления внутри функций производятся по мере необходимости и в том порядке, в каком в них возникает необходимость. В отличие от этого процедура описывает последовательность операций, которые выполняются обязательно и обязательно в указанном порядке. Поэтому способ записи, применяемый для определения функций, не годится для процедур, и для них используется специальная do-нотация, сходная с императивными языками (как С++ или Python).
Функции не могут вызывать процедуры, и это означает, что Haskell гарантирует отсутствие побочных эффектов в чистых вычислениях. По своему опыту могу сказать, что в первое время программировать с этим ограничением было неудобно, но потом привыкаешь и начинаешь просто думать по-другому, автоматически разделяя в уме алгоритмы чистых вычислений и императивную логику программы с тем, чтобы записать их отдельно друг от друга.
Процедуры в Haskell
Главная выполняемая функция в программе на Haskell - main - является процедурой, и на ней мы рассмотрим примеры описания процедур.
Простейшая процедура, выполняющая два действия. Как мы уже говорили, они выполняются строго в заданном порядке:
main = do print "Zdravstvuj, mir, eto ja!"
print "Haskell zzhot, C++ ...!"
"Действия", выполнямые в do - это в свою очередь вызовы других процедур. Мы также можем присваивать "переменным" значения, возвращаемые из этих процедур, и организовывать условное выполнение:
main = do print "Ej, parenj, kak tebja zvatj-to?"
name <- getLine
if name=="Bulat"
then do print "Blagodarju, Sozdatelj"
else do print ("Zdorovo, " ++ name)
Аналогичным образом можно применять и case:
main = do print "Ej, parenj, kak tebja zvatj-to?"
name <- getLine
case name of "Bulat" -> do print "Blagodarju, Sozdatelj"
"Deniok" -> do print "Blagodarju, Kosozdatelj"
_ -> do print ("Zdorovo, " ++ name)
Для организации циклов, как обычно, используется рекурсия.
Для возврата результата из процедуры используется return. Опишем рекурсивную процедуру, которая дожидается ввода непустой строки:
myGetLine = do str <- getLine
if str==""
then do print "Pozhalujsta, vvedite nepustuju stroku"
myGetLine
else return str
и пример её применения:
main = do print "Ej, parenj, kak tebja zvatj-to?"
name <- myGetLine
if name=="Bulat"
then do print "Blagodariu, Sozdatelj"
else do print ("Zdorovo, " ++ name)
Совершенно аналогично функциям, процедуры могут иметь входные параметры. Для описания чистых вычислений внутри процедур можно использовать let-блоки, однако вычисления в let-блоке не могут ссылаться на имена, определённые в нижеследующих действиях или let-блоках:
math x y = do
let x2 = x*2
x3 = x2*x
xy = x*y
print ("x=" ++ (show x))
print ("x v kvadrate=" ++ (show x2))
print ("x v kube=" ++ (show x3))
print ("proizvedenie x i y=" ++ (show xy))
main = do math 2 2
math 3 4
А теперь забацаем пример, который включает все вышеприведённые извраты:
...
Процедуры как параметры
Наконец, давайте вернёмся к истокам и вспомним, что "процедуры" в хаскеле - это всего лишь функции, которые могут иметь побочные эффекты, а функции в хаскеле являются "первоклассными" значениями. Это значит, что процедуры, как и любые другие функции, можно передавать в качестве параметров, сохранять в структурах данных, "добивать" параметрами и т.д. Различие всего одно - функция, применённая ко всем своим параметрами, является уже значением - это значение может храниться невычисленным только благодаря lazy evaluation. Процедура же, даже со всеми своими параметрами, остаётся процедурой (или если хотите действием) и выполняется ровно в тот момент, когда она вызвана в do-нотации. Пример:
main = do example (print "Privet!!")
example action = do print "Do.."
action
print "Posle.."
Здесь "Привет!!" печатается не в момент вызова example (для него операция печати - это всего лишь пассивный параметр), а в тот момент, когда вызов action вставлен в do-последовательность.
Немного глубже в do
Поначалу кажется, что do - какая-то чёрная магия, или встроенный синтаксис для записи процедур. На самом деле всё это просто cинтаксический сахар, без которого можно обойтись, но который позволяет склеивать процедуры и делает их написание проще. Но об этих тонкостях лучше прочитать в статье о монадах.
Библиотеки
Работа с файлами
- (english) Описание в Gentle Introduction
- (english) Описание в Tackling the Awkward Squad
Императивные массивы и хеши
- (english) Описание работы с иммутабельными массивами в Gentle Introduction
- (english) Описание в Modern array libraries
Обработка исключений (exceptions) и перехват сигналов ОС
- (english) Описание в Gentle Introduction
- (english) Описание в Tackling the Awkward Squad
Многопоточное программирование
- (english) Описание в Tackling the Awkward Squad
STM (Software Transactional Memory) - новый способ многопоточного программирования
- (english) Всё об STM
Ссылки
- (на русском)
- (english)
- IO_inside рассказывает о внутреннем устройстве монады IO, приводит множество практических примеров императивного программирования в Haskell и содержит дополнительные ссылки
- Tackling the Awkward Squad описывает императивное программирование, взаимодействие с языком C, многопоточное программирование и обработку исключений (exceptions)