Функциональное программирование на языке haskell
Основы функционального программирования/Основы языка Haskell
Содержание
Настоящая лекция будет полностью посвящена синтаксису языка Haskell (далее, для удобства, «Хаскел»). Будут рассмотрены все важнейшие понятия языка, их соотношение с уже́ изученными понятиями (на основе абстрактного функционального языка). Также по возможности будут приводиться примеры на Лиспе, чтобы не отрываться от основ и традиции.
Структуры данных и их типы [ править ]
Одна из основных единиц любого языка программирования — это символ. Символом традиционно называется последовательность букв, цифр и специальных знаков ограниченной или неограниченной длины. В некоторых языках строчные и прописные буквы различаются (к ним относится Хаскел), в некоторых нет (Лисп).
Символы чаще всего являются идентификаторами — именами констант, переменных, функций. Значениями же констант, переменных и функций являются типизированные последовательности знаков. Пример: значением числовой константы не может быть строка из букв. В функциональных языках есть понятие атом. В реализациях атомами называются символы и чи́сла, причём чи́сла могут быть трёх видов: целые, с фиксированной и с плавающей точкой.
Списочные структуры в Лиспе и Хаскеле описываются в соответствии с нотацией: заключением одного списка в другой. При этом в нотации Лиспа сделано послабление, так как перед скобкой внутреннего списка можно не ставить пробел.
Как говорилось во вводной лекции, типы данных в функциональных языках определяются автоматически. Механизм автоматического определения встроен и в Хаскел, но в некоторых случаях нужно явно указывать тип, иначе интерпретатор может запутаться в неоднозначности. В Хаскеле используется специальный символ :: (два двоеточия), который читается как «имеет тип». То есть если написать:
Это будет читаться как «Числовая константа 5 имеет тип Целое число».
Соглашения по именованию [ править ]
В Хаскеле важны соглашения по именованию, ибо они явно входят в синтаксис языка (чего обычно нет в императивных языках). Самое важное соглашение — использование прописной буквы в начале идентификатора. Имена типов, в том числе и определяемых разработчиком, должны начинаться с прописной буквы. Имена функций, переменных и констант должны начинаться со строчной буквы. В начале идентификатора могут ещё быть некоторые специальные знаки, некоторые из которых могут влиять на семантику идентификатора.
Определители списков и математические последовательности [ править ]
Пожалуй, Хаскел — единственный язык программирования, позволяющий просто и быстро конструировать списки, основанные на какой-нибудь простой математической формуле. Этот подход уже́ был использован при построении функции быстрой сортировки списка методом Хоара (см. пример 3 в лекции 1). Наиболее общий вид определителей списков выглядит так:
Бесконечные структуры данных можно определять на основе бесконечных списков, а можно использовать механизм рекурсии. Рекурсия в этом случае используется как обращение к рекурсивным функциям. Третий способ создания бесконечных структур данных состоит в использовании бесконечных типов.
Пример 11. Определение типа для представления двоичных деревьев
В этом примере показан способ определения бесконечного типа. Видно, что без рекурсии тут не обошлось. Если нет необходимости создавать новый тип данных, бесконечную структуру можно получить через функции:
Первая функция определяет бесконечную последовательность, полностью состоящую из единиц. Вторая функция возвращает последовательность целых чисел, начиная с заданного. Третья возвращает бесконечную последовательность квадратов натуральных чисел вместе с нулём.
Вызовы функций [ править ]
В Хаскеле нет нужды обрамлять вызов функции в виде списка. Например, если определена функция, складывающая два числа:
То её вызов с конкретными параметрами (например, 5 и 7 ) будет выглядеть как:
Здесь видно, что нотация Хаскела наиболее сильно приближена к нотации абстрактного математического языка. Однако он пошел ещё дальше Лиспа в этом вопросе, и в нём есть нотация для описания некаррированных функций, то есть тип которых нельзя представить в виде A 1 → ( A 2 → … ( A n → B ) … ) <\displaystyle A_<1>\rightarrow (A_<2>\rightarrow \ldots (A_. И эта нотация, как и в императивных языках программирования, использует круглые скобки:
Как видно, здесь использована инфиксная запись операции prefix — двоеточие, только такая запись используется в нотации Хаскела для обозначения или конструирования пары. После приведённого выше определения можно произвести вызов
Использование λ-исчисления [ править ]
Функциональная парадигма программирования основана на λ-исчислении, поэтому вполне закономерно, что все функциональные языки поддерживают нотацию для описания λ-абстракций. Хаскел не обошёл стороной этот аспект, если есть необходимость в определении какой-либо функции через λ-абстракцию. Ещё через λ-абстракции можно определять анонимные функции (например, для единичного вызова). Ниже показан пример определения функций add и inc именно при помощи λ-исчисления.
Пример 12. Функции add и inc, определённые через λ-абстракции
Пример 13. Вызов анонимной функции
Пример 13 показывает вызов анонимной функции, возводящей в куб переданный параметр. Результатом выполнения этой инструкции будет бесконечный список кубов целых чисел, начиная с нуля. Необходимо отметить, что в Хаскеле используется упрощённый способ записи λ-выражений; в точной нотации функцию add правильней было бы написать как:
Инфиксный способ записи функций [ править ]
Для некоторых функций возможен инфиксный способ записи. Это обычно простые двоичные операции. Вот как, например, определены операции конкатенации списков и композиции функций:
Пример 14. Инфиксная операция конкатенации списков
Пример 15. Инфиксная операция композиции функций
Покуда инфиксные операции всё-таки являются функциями в смысле Хаскела, то есть они каррированы, то имеет смысл обеспечить возможность частичного применения таких функций. Для того имеется специальная запись, которая в Хаскеле называется «секция»:
Это три секции, каждая из которых определяет инфиксную операцию конкатенации списков в соответствии с количеством переданных ей аргументов. Использование круглых скобок в записи секций обязательно.
Haskell: навстречу функциональному программированию
Haskell — это функциональный язык программирования, разработанный специально для обработки символьных вычислений и списков.
Данная статья носит обучающий характер и предназначена для новичков, стремящихся понять основные положения Haskell.
Что такое Haskell?
Haskell — широко распространенный чистый функциональный язык.
Функциональные программы больше подходят для использования в многопоточной среде и предусматривают параллельное выполнение, добиваясь более точной и эффективной производительности. В традиционных языках программирования мы инструктируем компилятор, как выполнить то или иное действие.
Но в Haskell действует иной принцип — здесь мы говорим, что делать. Кроме того, это ленивый язык, поэтому программа не выполняет код, если считает его необязательным.
Программа на Haskell — это не что иное, как ряд выполняющихся функций.
Haskell — строго типизированный язык. Это значит, что компилятор достаточно сообразителен, чтобы определить тип объявленной переменной. Следовательно, у нас нет необходимости явно указывать ее тип.
Настройка среды Haskell
Для настройки среды Haskell на компьютере Windows перейдите по этой ссылке на официальный веб-сайт и загрузите соответствующий установочный пакет.
Если же необходимо настроить эту среду в системе MAC, воспользуйтесь следующей ссылкой для перехода на официальный сайт и загрузите установщик Mac.
Процесс настройки в Linux выглядит следующим образом:
Начало работы с Haskell
Вы без особого труда начнете программировать на Haskell — достаточно лишь ввести следующую команду в терминал:
Вот теперь можно писать код.
Пример программы
Это простой пример, демонстрирующий динамизм Haskell. Рассмотрим следующий код:
Prelude> main = putStrLn «Hello Medium»
Основные операции
Haskell — настолько смышленый язык, что распознает число как число. Поэтому вам не нужно извне указывать его тип, как это обычно принято в других языках программирования.
Символы
Наряду с числами, Haskell способен распознавать символы, поступающие в него при вводе данных.
Для отображения типа переменной можно ввести следующую строку кода
и получить вот такой результат:
Строки
Строка — это не что иное, как набор символов. Создадим следующую строку
Если нужно узнать тип этой строки, снова воспользуйтесь :t :
Логические типы данных
С логическими типами всё также просто и понятно, как и с другими данными. Следующий пример демонстрирует некоторые из них.
Списки
Подобно другим типам данных в Haskell, List также обладает не меньшей практической значимостью. Как следует из примера, [a,b,c] представляет собой список символов. Следовательно, List — это набор элементов одного и того же типа данных, разделенных запятыми. Создадим свой список:
Метод Length
Списки имеют несколько методов. Допустим, у вас есть список x, длину которого необходимо определить. Следующий метод как раз для вас:
Метод Reverse
Этот метод используется для обращения порядка следования элементов списка:
Метод Add
Для добавления элемента в список можно использовать оператор ++ :
Этот код объединяет два списка: x и y.
Метод Delete
Используйте drop для удаления одного элемента из списка:
Кортеж
Haskell предоставляет еще один способ объявления нескольких значений в одном типе данных. Речь идет о кортеже, который можно рассматривать как список. Однако между ними есть ряд технических отличий.
Кортеж содержит фиксированное число элементов. Он относится к неизменяемым типам. Создается кортеж следующим образом:
Это кортеж содержит три элемента: два числа и символ.
Как и в случае со списками, у кортежей есть методы, например, для определения в нем первого или последнего элемента.
Первый элемент
Для извлечения первого элемента кортежа используется следующий метод:
Методы Head and tail
Метод head также предназначен для извлечения первого элемента кортежа:
Метод tail позволит вам извлечь не только последний или второй элемент кортежа, но и все его элементы за исключением первого:
Условные инструкции
Условные инструкции — это программная возможность, позволяющая применять условие в процессе работы кода. Программист может выполнить набор инструкций в зависимости от предварительно заданного условия.
Haskell располагает такими условными инструкциями, как:
Инструкция if-else
Синтаксис выражений if выглядит следующим образом:
Функции
Функции играют важную роль в Haskell, поскольку он является языком функционального программирования. Подобно другим языкам, в нем существует определение и объявление функций.
Следующий небольшой пример с функцией add поможет нам подробно разобраться в сути этой концепции.
Сопоставление с образцом
Этот метод предполагает сопоставление с конкретным типом выражения и позволяет упростить код.
Рассмотрим следующий блок кода:
В данном примере метод сопоставления с образцом использовался для вычисления факториала числа.
Охранные выражения
Принцип охранных выражений очень похож на сопоставление с образцом, но вот используются они для тестирования свойств выражений. В следующем примере кода в программу факториала были внесены изменения с учетом концепции охранных выражений:
Рекурсия
Рекурсия — это ситуация, при которой функция вызывает саму себя. Haskell не предоставляет возможность повторять цикл выражения более одного раза.
Он предлагает вам разбить всю функциональность на несколько разных функций и реализовать ее, используя рекурсию.
В данном примере для вычисления факториала 4 использовались оба метода: сопоставление с образцом и рекурсия.
Лямбда-выражения
Модули
Если у вас был опыт программирования на Java, то вы в курсе, что все классы объединяются в папки, так называемые пакеты. Подобно этому и Haskell можно рассматривать как набор модулей.
Например, предлагаю вашему вниманию модуль list:
И сразу функциональности List попадают в ваше распоряжение.
Среди других модулей можно выделить следующие:
Пользовательские модули
Создадим модуль Custom и определим в нем несколько функций.
А теперь импортируем его программу, выполнив следующее действие:
Полезные ресурсы:
Заключение
Надеюсь, что материал данной статьи помог вам составить общее представление о Haskell, и теперь вы без особого труда сможете написать на нем простой код.
Полагаю, что овладение Haskell упростит вам изучение других функциональных языков программирования.
Конспекты лекций «Haskell как первый язык программирования». Часть1
Привет Habr! Сегодня я достал свои старые конспекты по курсу «Haskell как первый язык программирования» Сергея Михайловича Абрамова и попробую максимально доходчиво и с примерами рассказать об этом замечательном языке тем, кто с ним еще не знаком. Рассказ ориентирован на неподготовленного читателя. Так что, даже если вы впервые услышали слово Haskell…
Базовые типы Haskell
Базовые типы языка Haskell — это:
Числа
Логические величины
Символы
Списки
Упорядоченные множества (tuples)
Функции
Числа
Целые:
Integer (-∞,∞)
Int (-2^31, 2^31-1)
В прелюдии (стандартной библиотеке) определенно много полезных функций для целых чисел, в том числе, и преобразование в число с плавающей точкой (fromInt и fromInteger)
Числа с плавающей точкой:
Float (7 знаков после запятой)
Double (16 знаков после запятой)
Логические величины
Bool (True | False)
Операции конъюнкции, дизъюнкции и отрицания (&&, ||, not)
Символы
Char (’a’)
И функции Char в Int и Int в Char (ord, chr)
Несколько стандартных операций в примерах:
Main> head [1,2,3]
1
Main> tail [1,2,3]
[2,3]
Main> length [True,False]
2
Main> reverse [1,2,3]
[3,2,1]
Main> 0:[1,2,3]
[0,1,2,3]
Main> — строка приглашения в консоли компилятора ghci
«:» — операция присоединения элемента к списку.
Упорядоченные множества
Примеры:
(2.4, ”cat”) (Float, [Char])
(’a’, True, 1) (Char, Bool, Int)
([1,2],sqrt) ([Int], Float->Float)
(1, (2, 3)) (Int, (Int, Int))
Но, сердце Haskell и всего функционального программирования — это, конечно, сами функции!
Функции
Функция, в современной математике, это закон соответствия, который сопоставляет каждому элементу x из данного множества A один единственный (или ни одного) элемент y из множества B.
Haskell, по своему назначению, это, прежде всего, язык математиков, поэтому синтаксис тут максимально точно соответствует этому определению.
Пример:
Как вы, наверное, догадались, это функция возведения числа в квадрат. Разберем её подробно:
Вторая строка — это определение функции:
Имя_функции параметры = правило_вычисления
square x = x*x
Функция без параметров есть ничто иное, как константа:
Функция с несколькими параметрами:
Спасибо janatem за разъяснения (читай комментарии).
Определения функций с альтернативами
Как и в любом языке, в Haskell есть конструкции ветвления.
Разберем их на примере функции abs (модуль).
If … then … else …
Но, помимо стандартных if и case, в Haskell есть очень красивая и наиболее используемая конструкция ветвления. Так называемые, охранные выражения. Пример:
AdBlock похитил этот баннер, но баннеры не зубы — отрастут
Читают сейчас
Редакторский дайджест
Присылаем лучшие статьи раз в месяц
Скоро на этот адрес придет письмо. Подтвердите подписку, если всё в силе.
Похожие публикации
Почему функциональное программирование такое сложное
Не морочьте мне голову со своим функциональным программированием
История языков программирования: как Haskell стал стандартом функционального программирования
Курсы
AdBlock похитил этот баннер, но баннеры не зубы — отрастут
Минуточку внимания
Комментарии 42
В этой статье используется очень прагматичный подход к ознакомлению, которые не затрагивает ряд догматичных аспектов языка.
Например, говорится, что к базовым типам относятся Числа, Логические величины, Символы, Списки, Упорядоченные множества (tuples) и Функции. По какому признаку одни типы относятся к базовым, а другие — нет? Возможные видимые варианты ответа:
2. как реализуемые особым образом типы? Например, список — участок памяти, Bool — всем привычный bool, Int — всем привычный 4-байтный int. Если так, то почему сюда не включен IO? Да и зачем в языке столь высокого уровня с первых шагов акцентировать внимание на такой конкретике.
Кроме этого момента есть ещё некоторые минусы, вроде смешивание рассказа о языке и о ghci, скачки от одной темы к другой, отсутствие единой линии (плана) повествования, малое число примеров, демонстрирующих объясняющие слова.
Я не знаю, на кого рассчитана статья. И несмотря на это, хорошо, что появляются новые туториалы по языку. Продолжение не будет лишним, но мне кажется, что лучше собрать уже имеющуюся русскоязычную литературу и опираться на неё во избежание повторения уже написанного и изложенного, но ради дополнения.
Функциональное программирование на Haskell : Часть 1. Введение
Цикл статей адресован читателю, знакомому с программированием, но не знакомому с функциональным подходом. Первые статьи будут затрагивать базовые понятия. Далее мы перейдем к особенностям синтаксиса и семантики Haskell и практическим вопросам. В первой статье мы вкратце расскажем о функциональном программировании, полезных источниках информации, а также реализациях Haskell.
Хорошая статья, добавил в закладки.
Как и всегда, добротно и вкусно. Начинающим будет полезно.
это ты их похвалил или обозвал?
Снятся ли петросянам вроде тебя IBM-овцы?
Тем, кому нужно введение в хаскель, не помешает еще знать, зачем им нужен хаскель. Тема не раскрыта.
Вполне годная статья.
> Снятся ли петросянам вроде тебя IBM-овцы?
только неумеющим писать, вроде тебя
> зачем им нужен хаскель. Тема не раскрыта
прошли функциональное программирование в универе год назад (на 4м курсе)
преподавали нам, правда, пролог
Ответа на этот вопрос не существует.
Не Душкин и то хорошо. А в целом Хаскелл не нужен.
Капитан Очевидность напоминает, что пролог является языком не функционального программирования, а логического.
> в целом Хаскелл не нужен
> А в целом Хаскелл не нужен.
> Как и всегда, добротно и вкусно. Начинающим будет полезно.
ООо! Слава бимерам. Теперь даже закоренелые похиписты смогут вести высокоинтеллектуальные беседы на ЛОРе! Я счастлив
Мне, скажем так, кажется проблематичным будущее языка где для одной из базовых операций типа DO from to вводится понятие монады, вокруг которой вот уже пару лет пасутся просто таки горы фриков с удовольствием объясняющих другу другу какая это крутая штука и как она полезна. Знаю, вы будете расказыать о DSL и критических приложениях, упомяните и atom и бетономешалку, а также подразделение Свисс-банка, где используется Хаскелл, ну и пусть. Массовым он не будет и точка. StateMonad это то что не позволить ему таким стать (для начала).
Да и кстати, после книг Худака и Сулливана совершенно не понятно зачем писать какие-то статьи по сабжу.
Грамма-наци негодуэ! Слово «даже» нужно было выделить запятыми!
Нельзя доказать ненужность. Ненужность подразумевается по умолчанию, пока не доказана нужность.
>>ООо! Слава бимерам. Теперь даже закоренелые похиписты смогут вести высокоинтеллектуальные беседы на ЛОРе! Я счастлив
В определённых кругах твою реакцию назвали бы баттхертом.
Да вот именно. Именно не просто далеко, а далеко-далеко не всем. Да и кстати, удивляет полный игнор сообществом свежей книги Худака «The Haskell School of Musik» которая по моему есть в сободном доступе. Во всяком случае у Бешенова в «ресурсах» её нет. А жаль.
Спасибо, обязательно прочту.
Грамма-наци негодуэ! Слово «даже» нужно было выделить запятыми!
Врёшь, не нужно. Хотя теоретически можно.
знакомый автор, хорошая статья
Маразм это когда кто-то рассуждает о наличии или отсутствии профита не зная сабжа.
Сервер web приложений теперь называется не happs, а happstack
использование монады Error (ErrorT) для получения параметров https запросов
Несколько лет изучать теорию категорий что бы записать получение параметров https запросов через монады? То, что любой школьник помусолив полгода php, напишет вермишелью бесплатно? Вот школьнику в результате и закажут создание ПО «бесплатно», а не бородатому к.т.н-у который запросит за свой хацкель 100000 руб/мес
На мой взгляд, вторую часть придётся ждать до морковкиного заговения. Как всегда, галопом по Европам.
> Несколько лет изучать теорию категорий что бы записать получение
параметров https запросов через монады? То, что любой школьник
помусолив полгода php, напишет вермишелью бесплатно? Вот школьнику в
результате и закажут создание ПО «бесплатно», а не бородатому к.т.н-у
который запросит за свой хацкель 100000 руб/мес
Именно так всё и происходит. В результате у заказчика проект не укладывается ни в бюджет, ни в сроки. Школьник в определённый момент начинает плеваться от своего кода и ему процесс разработки причиняет всё больше и больше головной боли, затраты на поддержку проекта постоянно увеличиваются. Качество программы при этом ужасно. Ну а к.т.н. если не попадёт под распил какого-нибудь гранта, то его судьба незавидна.
А ведь процесс разработки на haskell гораздо комфортнее, не нужно в голове удерживать так много информации, по причине применения грамотных абстракций. Поэтому многие согласились бы разрабатывать на этом языке даже за гораздо меньшую оплату, чем 100000 руб/мес, особенно взамкадье.
Впрочем, это несколько идеологические вопросы и здесь не может быть чёткой оценки.
Однако Galois, к примеру, совсем не страдает от неиспользования java или с++, а ведь им знакомы эти технологии. Многие же организации, использующие традиционные инструменты промышленного программирования, просто ничего не знают и знать не хотят о haskell.
> Поэтому многие согласились бы разрабатывать на этом языке даже за гораздо меньшую оплату, чем 100000 руб/мес
Эти многие и сами бы доплатили, лишь бы на ЛОРе похвастаться, мол на хаскеле кто-то где-то да пишет энтерпрайзнутые приложения 🙂
На русском чрезвычайно мало информации по haskell, боюсь без знания английского не обойтись.
Это ж не winfaq, к чему такие предупреждения?
После первого километра линуксовых мануалов человек начинает читать технический английский с той же лёгкостью, что и литературный русский 🙂
>Но нужен _только_ для вычислений
Если нужно считать быстро, то лучше уж на С. А если нужно считать удобно, то выше уже упомянули Octave и R. Хацкель явно нужен для чего-то другого. Жду следующих статей, может все же пойму, для чего же?
«ДАННЫХ НЕДОСТАТОЧНО ДЛЯ ОСМЫСЛЕННОГО ОТВЕТА.» © Мультивак
>Это ж не winfaq, к чему такие предупреждения? После первого километра линуксовых мануалов человек начинает читать технический английский с той же лёгкостью, что и литературный русский 🙂
Не-не-не, не с той же легкостью. Читаю мануалы на англицком и матюкаюсь каждый раз. Ну не люблю я языки с детства, включая русский. Но, русский сам выучился, а английкий еще учить надо.
Если нужно считать быстро, то лучше уж на С
Intel, AMD, IBM, NVIDIA, и даже Sun согнулись поперек, от смеха. LOL. Им-то спешить некуда, и поэтому они разрабатывают фреймворки для научных расчетов http://ppl.stanford.edu/wiki/index.php/Projects на. чем бы ты думал?
Ну конечно, не на C
Начал читать статью. Не могу понять, как работает программа из двух строк.
Здесь уравнений два. При вычислении функции уравнения просматриваются по порядку. Если n = 0, то будет использовано первое уравнение. Если n ≠ 0, то оно не подойдет, и задействуется второе.
Первый вопрос. Почему уравнений две штуки? Что означает «fac»? Это символьный идентификатор уравнения? Это символьный идентификатор функции, к которой принадлежат уравнения?
Второй вопрос. Каким образом происходит проверка «Если n = 0»? Я нигде этой проверки не вижу. Имеется в виду, что в такой записи вначале проверяется левый аргумент уравнения? Видимо левый 0 сравнивается с n, но где указано что 0 надо сравнивать с n? Если левый аргумент уравнения (0) совпадает с проверяемым значением (n), то проверяемому значению присваивается правый аргумент (1)? Но если так, то в чем смысл во втором уравнении сравнивать n с n?
> А в целом Хаскелл не нужен.
Сколько людей на земле скажут, что им нужен Хаскелл, а сколько людей на земле — что им нужен ты? Прикинь и сделай вывод.
> Не-не-не, не с той же легкостью. Читаю мануалы на англицком и матюкаюсь каждый раз. Ну не люблю я языки с детства, включая русский. Но, русский сам выучился, а английкий еще учить надо.
Тогда единственное, что тебе подходит — это 1С.
Почитай про pattern matching.
Почитай про pattern matching.
Почитай про pattern matching.
Какой-то нечеловеческий язык, однако. Ну хорошо, функции и рекурсия, для математики полезно (хотя и на С реализуется ничуть не сложнее).
Теория, опять же забавная.
А конкретные, практические задачи на нём решать можно? Очень математические, если уж на то пошло.
Вот есть сокет/файл. Из него сыплются байты. И хочется очень математически и функционально с этими байтами делать преобразование Фурье и то, что получилось выплёвывать в другой сокет/файл.
Что толку от этой функциональности, если её практически-то никак применить не получается?
>Просто расскажите как на этом Haskell’е сокет поллить и данные буферизовать, чтоб уж было чего функционально и математически обрабатывать?
Ну я бы тут подключил внешнюю библиотеку для управления сокетами(а скорее просто установил нужный модуль(с сетями на хаскеле дела не имел)).
Всё должно решатся просто. На XSLT ведь матрасчёты не делаются(я не про учасников спецолиппиад).