Язык Haskell: О пользе и вреде лени: различия между версиями

Содержимое удалено Содержимое добавлено
мНет описания правки
Строка 1:
:<small>Исходная версия статьи (Ворожцов &nbsp;А.&nbsp;В., "«Язык Haskell: О пользе и вреде лени"») была опубликована в [[Журнал "«Потенциал"»|журнале "«Потенциал"»]]</small>
 
=== Аннотация= ==
 
===Аннотация===
[[Изображение:2_1.jpg|right|]]
В большинстве российских школ на уроках программирования изучают
Строка 62 ⟶ 63 :
ячеек друг от друга, и в одной из ячеек получаем нужный нам
результат.
Программа на языке Haskell представляет
Строка 149:
<tt> (Float->Float)->Float</tt> соответствует функции, которая получает на вход функцию
типа <tt> Float->Float</tt> и возвращает действительное число.
 
Интересно заметить, что при конструировании новых типов с помощью операций
<tt> [a]</tt>, <tt> (a,b)</tt> и <tt> a->b</tt> не обязательно вместо <tt> a</tt> и <tt> b</tt>
Строка 159 ⟶ 158 :
и возвращает список пар элементов типа <tt> a </tt>и <tt> b</tt>.
 
=== Пример 1. ===
Функция <tt>inc<tt> увеличивает число на единицу. Она
определяется следующим образом:
Строка 173 ⟶ 172 :
inc :: Integer->Integer
 
=== Пример 2. ===
 
Функция <tt>add<tt> находит сумму данных ей чисел.
Аргументами этой функции являются два числа:
Строка 191:
разделяются просто пробелами, а не запятыми, как в языках Си или Паскаль.
 
=== Пример 3. ===
 
Есть два способа обозначения списков &mdash; квадратные скобки, в которых перечислены
элементы через запятую, или круглые скобки, в которых элементы разделены двоеточием. В частности
Строка 212 ⟶ 213 :
но показательный.
 
=== Пример 4. ===
 
Рассмотрим функцию <tt> map</tt>, которая получает на вход функцию и список элементов,
а в качестве результата возвращает список элементов, к которым применена данная функция:
Строка 234 ⟶ 236 :
<tt> map (\ x -> x*x*x ) [1,2,3] ) ''<math>\Rightarrow</math>'' [1,8,27].</tt>
 
А вот простой способ найти первые 5 степеней двоек:
 
<tt> take 5 powers where powers = map (2 ^) [1..] ''<math>\Rightarrow</math>'' [2,4,8,16,32].</tt>
 
Есть другой способ определения функции <tt> map</tt>:
&laquo;<tt> map f xs = [ f x | x <- xs]</tt>&raquo;.
Есть другой способ определения функции <tt> map</tt>:
Это соответствует математической записи <math>map(f,xs) = \left\{ f(x) | x\in xs \right\}.</math>
&laquo;<tt> map f xs = [ f x | x <- xs]</tt>&raquo;.
Конструкция &laquo;<tt> x<-xs</tt>&raquo; называется &laquo;генератором&raquo;, её
Это соответствует математической записи
<math>map(f,xs) = \left\{ f(x) | x\in xs \right\}.</math>
Конструкция &laquo;<tt> x<-xs</tt>&raquo; называется &laquo;генератором&raquo;, её
следует интерпретировать как &laquo;элемент <tt> x </tt>берётся из списка <tt> xs</tt>&raquo;.
 
Строка 283 ⟶ 282 :
Например, в языке Mathematica (http://wolfram.com) для определения функций используется &laquo;<tt> :=</tt>&raquo;,
а для присваивания &mdash; просто &laquo;<tt> =</tt>&raquo;.
 
Рассмотрим функцию <tt> numsFrom</tt>, которая получает один
Строка 305 ⟶ 302 :
<tt> take 5 squares ''<math>\Rightarrow</math>'' [0,1,4,9,16].</tt>
 
Функцию <tt>take</tt> можно было определить рекурсивно:
 
Строка 334 ⟶ 330 :
так как ''11 = 1 + 2 + 8 = 2^0 + 2^1 + 2^3'', а для ''255'' ответ должен быть равен 8,
так как ''255=1 + 2 + 4 + ... + 64 + 128'' есть сумма восьми различных степеней двойки.
 
 
Введём следующие определения.
Строка 350 ⟶ 345 :
Стандартная функция <tt> reverse</tt> обращает список: получает на вход список и возвращает тот же
список, в котором элементы идут в обратном порядке, начиная с последнего до первого:
 
 
{|
Строка 373 ⟶ 367 :
|8.
|}
 
 
Для того, чтобы найти сами степени двойки, в сумму которых разлагается
Строка 436 ⟶ 429 :
Несмотря на свою &laquo;серьёзность&raquo;, выглядит он подозрительно просто:
 
--Быстрая сортировка на языке Haskell
qsort [] = []
Строка 444 ⟶ 436 :
qsort [y | y <- xs, y>=x]
 
Поясним смысл этих строчек. Запись &laquo;<tt> qsort [] = [] </tt> &raquo; означает, что если на вход дан пустой
список <tt>[]</tt>, то и в результате будет пустой список. В следующей строчке рассматривается случай,
Строка 456 ⟶ 447 :
Тот же самый алгоритм на языке Си занимает далеко не
4 строчки:
 
/* Быстрая сортировка на языке Си */
void qsort( a, lo, hi )
{
int a[], hi, lo; {
int h, l, p, t;
Строка 486 ⟶ 477 :
qsort( a, lo, l-1 );
qsort( a, l+1, hi );
}
 
 
Таким образом, на языке Haskell многие алгоритмы можно запрограммировать существенно быстрее, нежели на Си или Паскале.
 
Строка 503 ⟶ 492 :
http://wtk.norilsk.net/pm/fp/haskell.html, http://www.haskell.ru/.
 
== Дистрибутивы Haskell ==
Строка 509 ⟶ 497 :
Все они распространяются бесплатно.
Рекомендуем начать с маленького и удобного для обучения интерпретатора Hugs (http://haskell.org/hugs).
 
== Зачем нужно функциональное программирование? ==
Строка 524 ⟶ 511 :
* создаются адаптивные, легко изменяемые и расширяемые программы.
 
Кроме того, отмечается, что благодаря строгой типизации языка, в программах на
Haskell не случается системных ошибок и не бывает аварийных ситуаций (<tt> сore dump</tt>).
Строка 539 ⟶ 525 :
Мощь, которую предоставляет это исчисление,
ещё не до конца осознана программистами и не в полной мере используется на практике.
Обратите внимание на то, что в списке достоинств не указаны такие
Строка 562 ⟶ 547 :
программирования в качестве одного из важных достоинств языка
указывают его близость к естественному языку (обычно английскому).
== Встроенное управление памятью ==
Строка 618 ⟶ 602 :
и &laquo;научить&raquo; трансляторы функциональных языков программирования &laquo;видеть их&raquo; в определениях функций
и применять известные эффективные алгоритмы вычисления.
Конечно, язык Си предпочтительнее в целом ряде случаев: системное программирование, драйверы устройств, приложения, от которых требуется высокая производительность, например, компьютерные игрушки с качественной графикой и др. Но всё это довольно специальные случаи.
Строка 634 ⟶ 617 :
Система работает на мейнфреймах IBM.
* Компания Ericsson разработала функциональный язык Erlang для создания системы управления телефонных станций.
* Исследователи в корпорации MITRE используют Haskell для прототипирования{{ref|cons}} приложений обработки цифровых сигналов.
 
Для многих программистов не секрет, что на процедурных языках
можно писать объектно-ориентированным образом, а на
Строка 673 ⟶ 654 :
 
== Примечания ==
 
# {{note|cons}} Когда в языке Си определяется переменная типа функции, необходимо указать типы аргументов функции и тип возвращаемого значения. Например, &laquo;<tt> int (*)(int, int) </tt>&raquo; &mdash; это тип функции, возвращающей число типа <tt> int </tt> и принимающей в качестве аргументов два числа типа <tt> int </tt>.
# {{note|cons}} Прототипирование &mdash; это быстрая &laquo;черновая&raquo; реализация базовой функциональности для анализа работы системы в целом. Для прототипирования используют языки высокого уровня абстракции (Java, Perl, Python, Haskell, ...). После этапа прототипирования обязательно следуют этапы пересмотрения архитектуры системы, разработки, реализации и тестирования конечного продукта. На этапе разработки подготавливают систему тестов, по работе которых буду судить о качестве продукта. При реализации решения обычно используют другой, &laquo;более машинноориентированный&raquo; язык программирования (Си, Си++, ...), пишут более аккуратный,документированный код, а на тестирование системы тратят сравнительно большое количество усилий для достижения качественного результата.