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

Содержимое удалено Содержимое добавлено
мНет описания правки
Строка 2:
{{ОФП Содержание}}
 
Настоящая лекция будет полностью посвящена синтаксису языка [[w:Haskell|Haskell]] (далее, для удобства, «Хаскел»). Будут рассмотрены все важнейшие понятия языка, их соотношение с уже́уже́ изученными понятиями (на основе абстрактного функционального языка). Также по возможности будут приводиться примеры на [[w:Лисп|Лиспе]], чтобы не отрываться от основ и традиции.
 
== Структуры данных и их типы ==
Строка 8:
Одна из основных единиц любого языка программирования — это символ. Символом традиционно называется последовательность букв, цифр и специальных знаков ограниченной или неограниченной длины. В некоторых языках строчные и прописные буквы различаются (к ним относится Хаскел), в некоторых нет (Лисп).
 
Символы чаще всего являются идентификаторами — именами констант, переменных, функций. Значениями же констант, переменных и функций являются типизированные последовательности знаков. Пример: значением числовой константы не может быть строка из букв. В функциональных языках есть понятие '''атом'''. В реализациях атомами называются символы и чи́слачи́сла, причём чи́слачи́сла могут быть трёх видов: целые, с фиксированной и с плавающей точкой.
 
[[w:Список (структура данных)|Список]] — ещё одно понятие функционального программирования. В абстрактной математической записи использовались квадратные скобки <code>[]</code>, которые также используются в Хаскеле. Но в Лиспе используются обычные, круглые скобки <code>()</code>. Элементы списка в Лиспе разделяются [[w:пробел|пробелами]], что не очень наглядно, поэтому в Хаскеле ввели запятую для разделения. Список <code>[a, b, c]</code> будет правильно записан в синтаксисе Хаскела, а в нотацию Лиспа его необходимо перевести как <code>(a b c)</code>. Создатели Лиспа пошли ещё дальше в своей изощрённости: допускается использовать точечную запись для организации пары, поэтому приведённый выше список можно записать как <code>(a.(b.(c.NIL)))</code>.
 
Списочные структуры в Лиспе и Хаскеле описываются в соответствии с нотацией: заключением одного списка в другой. При этом в нотации Лиспа сделано послабление, т.&nbsp;к.так как перед скобкой внутреннего списка можно не ставить пробел.
 
Как говорилось во '''[[Основы функционального программирования/Вводная лекция|вводной лекции]]''', типы данных в функциональных языках определяются автоматически. Механизм автоматического определения встроен и в Хаскел, но в некоторых случаях нужно явно указывать тип, иначе [[w:интерпретация|интерпретатор]] может запутаться в неоднозначности. В Хаскеле используется специальный символ <code>::</code> (два двоеточия), котрый читается как «имеет тип». Т.&nbsp;е.То есть если написать:
 
<code>5 :: Integer</code>
Строка 20:
Это будет читаться как «Числовая константа <code>5</code> имеет тип Целое число».
 
Ещё Хаскел поддерживает [[w:полиморфизм|полиморфные]] типы, или шаблоны типов. Если, например, записать <code>[a]</code>, то это будет обозначать тип «список из атомов любого типа», причемпричём тип атомов должен быть один во всём списке. Списки <code>[1, 2, 3]</code> и <code>[‘a’'a', ‘b’'b', ‘c’'c']</code> будут иметь тип <code>[a]</code>, а список <code>[1, ‘a’'a']</code> будет другого типа. В этом случае в записи <code>[a]</code> символ <code>a</code> имеет значение типовой переменной.
 
== Соглашения по именованию ==
Строка 28:
== Определители списков и математические последовательности ==
 
Пожалуй, Хаскел — единственный язык программирования, позволяющий просто и быстро конструировать списки, основанные на какой-нибудь простой математической формуле. Этот подход уже&#769;уже́ был использован при построении функции быстрой сортировки списка методом Хоара (см. '''[[Основы_функционального_программирования/Вводная_лекция#Краткость и простота|пример 3]]''' в '''[[Основы_функционального_программирования/Вводная_лекция|лекции 1]]'''). Наиболее общий вид определителей списков выглядит так:
 
<code>[ x | x <- xs ]</code>
Строка 73:
<code>add 5 7</code>
 
Здесь видно, что нотация Хаскела наиболее сильно приближена к нотации абстрактного математического языка. Однако он пошел ещё дальше Лиспа в этом вопросе, и в нём есть нотация для описания некаррированных функций, т.&nbsp;е.то есть тип которых нельзя представить в виде <math>A_{1}A_1 \rightarrow (A_{2}A_2 \rightarrow \ldots (A_{n}A_n \rightarrow B) \ldots )</math>. И эта нотация, как и в императивных языках программирования, использует круглые скобки:
 
<code>add (x, y) = x + y</code>
Строка 82:
inc = add 1</code>
 
Т.&nbsp;е.́То есть в этом случае вызов функции <code>inc</code> с одним параметром просто приведёт к вызову функции <code>add</code> с двумя, первый из которых — <code>1</code>. Это интуитивное понимание понятия частичного применения. Для закрепления понимания можно рассмотреть классический пример: функция <code>map</code> (её определение на абстрактном функциональном языке приведено во '''[[Основы функционального программирования/Структуры данных и базисные операции|второй лекции]]'''). Вот определение <code>map</code> на Хаскеле:
 
<code>map :: (a -> b) -> [a] -> [b]
Строка 112:
<code>add = \x -> \y -> x + y</code>
 
Остаётся отметить, что тип λ-абстракции определяется точно так же, как и тип функций. Тип λ-выражения вида <math>\lambda x.\operatorname{expr}</math> будет выглядеть как <math>T_{1}T_1 \rightarrow T_{2}T_2</math>, где <math>T_{1}T_1</math> — это тип переменной <math>x</math>, а <math>T_{2}T_2</math> — тип выражения <math>\operatorname{expr}</math>.
 
== Инфиксный способ записи функций ==
Строка 137:
Это три секции, каждая из которых определяет инфиксную операцию конкатенации списков в соответствии с количеством переданных ей аргументов. Использование круглых скобок в записи секций обязательно.
 
Если функция принимает два параметра, то её также можно записывать в инфиксной форме. Однако если просто записать между параметрами имя функции, это будет ошибкой, ибо в строгой нотации Хаскела это будет просто двойным применением, причём в одном применении не будет хватать одного операнда. Чтобы записать функцию в инфиксной форме, её имя необходимо заключить в символы обратного апострофа — <code>[[w:`Гравис|`]]</code>.
 
Для вновь определённых инфиксных операций возможно определение порядка вычисления. Для этого в Хаскеле есть зарезервированное слово <code>infixr</code>, которое назначает операции приоритет выполения в интервале от 0 до 9: 9 объявляется самой сильной степенью значимости. 10 также входит в этот интервал, и именно эту степень имеет операция применения). Вот так определяются степени для определенных в примерах 14 и 15 операций:
Строка 144:
infixr 9 .</code>
 
В Хаскеле все функции являются нестрогими, что значит: все они поддерживают отложенные вычисления. Если какая-то функция определена как <code>bot = bot</code>, то её вызов даст ошибку, и такие ошибки обычно сложно отслеживать. Но если есть некая константная функция, которая определена как <code>constant_1 x = 1</code>, то при вызове конструкции <code>(constant_1 bot)</code> никакой ошибки не произойдёт, т.&nbsp;к.так как значение функции <code>bot</code> в этом случае не вычислялось бы (вычисления отложенные, значение вычисляется только тогда, когда оно действительно требуется). Результатом вычисления, естественно, будет <code>1</code>.
 
== Упражнения ==