erlang язык программирования обучение для чайников
Erlang для самых маленьких. Глава 3: Базовый синтаксис функций
Доброй ночи, Хабр! Мы продолжаем изучение Erlang для самых маленьких.
В прошлой главе мы рассмотрели как объявлять функции и как объединять их в модули. В этой главе мы рассмотрим синтаксис функций более подробно.
Исходники примеров к главе лежат здесь.
Сопоставление с образцом
Для начала давайте напишем функцию, которая будет приветствовать пользователя и текст приветствия будет зависеть от его пола. В виде псевдокода наша функция будет выглядеть следующим образом:
Если вместо классической конструкции if then else использовать сопоставление с образцом, можно сэкономить кучу шаблонного кода. Вот так эта функция будет выглядеть на Erlang если использовать сопоставление с образцом:
Функция io:format() используется для форматированного вывода в терминал. Здесь мы использовали сопоставление с образцом в описании списка аргументов функции. Это позволило нам одновременно присвоить входные значения и выбрать ту часть функции, которая должна быть выполнена. Зачем сначала присваивать значения, а потом сравнивать их в теле функции, если можно сделать это одновременно и в «более декларативном» стиле?
В общем виде объявление такой функции выглядит следующим образом:
Каждая ветвь функции объявляется как полноценная функция но заканчивается точкой с запятой( ; ), следом за ней объявляется следующая. После последней части ставится точка.
Обратите внимание на последний образец. Что будет, если мы вызовем нашу функцию greet() и укажем непредусмотренный пол? Мы получим исключение о том, что входные параметры не подходят ни под один из образцов:
Поэтому важно включать в объявление образец, который подойдет под любое значение. При этом он должен быть последним. В противном случае образцы объявленные после него никогда не будут обработаны.
Давайте перепишем нашу функцию, так что бы она корректно обрабатывала неверные входные значения:
Но сопоставление с образцом в объявлении функций приносит намного больше пользы, чем просто сокращение объема кода. Вспомним списки: список состоит из головы и остальной части. Давайте напишем две функции, которые будут возвращать первый и второй элементы полученного списка.
Достаточно просто, не так ли? Есть еще один интересный прием, основанный на том факте, что переменные в Erlang можно присвоить только один раз.
Охранные выражения
У сопоставления с образцом есть один большой недостаток: оно недостаточно выразительно. С его помощью нельзя указать тип данных, диапазон и другие подобные обобщения. Каждый образец является конкретным случаем. Для решения этой проблемы в Erlang есть Охранные выражения (или сторожевые условия). Для начала давайте напишем функцию, которая будет принимать наш ИМТ (индекс массы тела) и оценивать наше состояние. ИМТ человека равен его весу разделенному на квадрат роста.
Здесь при обращении к функции происходит проверка первого условия, находящегося после слова when ( Bmi = ). Если это выражение возвращает true будет выполнена соответствующая ветвь кода. Иначе происходит проверка следующего условия и так до конца. И тут мы тоже добавили в конец условие, под которое подойдет любое значение.
В общем случае объявление функции с использованием охранных выражений выглядит следующим образом:
Если достаточно хотя бы одного (аналог orelse ), они разделяются точкой с запятой( ; ).
В охранных выражениях можно использовать функции. Вот функция, которая делит одно число на другое, но перед этим проверяет, что бы переданные аргументы были числами и что бы Y не был равен нулю:
Ветвление
Оператор if
В общем случае оператор ветвления if выглядит следующим образом:
Хочу напомнить, что в отличии от Haskell, здесь отступы не играют никакой роли кроме декоративной и код отформатирован так только того, что бы его было легче воспринимать. Вы вольны форматировать его так, как вам будет удобно.
Здесь выражения rule_1 и rule_2 — это одно или несколько условий. В случае успешного выполнения условия, будет выполнен блок кода Expression (может содержать одну или несколько команд) идущий за ним. Таких логических ветвей может быть сколько угодно.
Давайте для примера напишем функцию, которая будет принимать кортеж состоящий из температуры и названия шкалы ее измерения и на основании этих данных оценивать ее.
Как мы видим, выражение case of почти полностью можно переместить в область объявление функции. Так где же лучше размещать условия? Ответ прост: там где вам больше нравиться. Различия между этими двумя конструкциями минимальны. Поэтому используйте тот вариант, который вам легче читать.
Заключение
В этой главе мы рассмотрели то, как можно управлять потоком выполнения внутри функций. Узнали, какие для этого существуют конструкции и как их использовать. Так же мы познакомились с охранными выражениями.
В следующей главе мы более пристально рассмотрим систему типов в языке Erlang.
Спасибо, что дочитали до конца. Надеюсь, было интересно. Прошу прощения за столь большую задержку. Времени свободного совсем нет.
Введение в Erlang
Ранее на блоге я публиковал две заметки по языку Erlang — «Обработка списков на Erlang» и «Unit тестирование в Erlang на примере«. Я полагал, что это будет интересно тем, кто интересуется Erlang-ом (обычно им интересуются как языком параллельного и распределенного программирования и я рекомендую посмотреть на этот язык своим студентам на соответствующем курсе). Однако, я не учел, что интересующиеся купят нормальную книгу или, по крайней мере, найдут полноценную серию уроков (от и до), а не будут собирать информацию с разных источников. Поэтому я решил добавить введение в Erlang.
1 Erlang. Операторы. Atoms, Integers, Booleans.
Математические операторы
Тип | Описание | Тип данных |
---|---|---|
+ | Сложение (addition) | Integer | Float |
— | Вычитание (substraction) | Integer | Float |
* | Умножение (multiplication) | Integer | Float |
/ | Деление с плавающей запятой (floating point division) | Integer | Float |
div | Деление нацело (integer division) | Integer |
rem | Остаток от деления (integer remainder) | Integer |
Примеры использования математических операторов в консоли erlang:
Атомы (константы)
Атомы это строковые константы типа: red, green, blue или enabled, disabled. Всегда должны начинатся с нижнего регистра (маленькой буквы) и могут включать в себя буквы, цифры, символы «@», «_», «.». Например: testUser, test_user, test.user. В принципе для атомов можно использоват любые символы, если заключать их в одинарные кавычки: ‘Monday’, ‘not ready’, ‘node#1\nnode#2’
Integers (целые)
Определение целых работа с ними классическая:
Booleans
Здесь тоже все более-менее привычно за исключением того что это не отдельный тип а просто обычный атом-константа. Вот несколько примеров, которые иллюстрируют особенности erlang:
Оператор | Описание |
---|---|
and | Возвращает true если оба аргумента по отдельности вернут true |
andalso | Возвращает false если первый аргумент был false и не проверяет второй аргумент |
or | Возвращает true если любой аргумент был true |
orelse | Возвращает true если первый аргумент был true, без проверки второго элемента |
xor | Возвращает true если один из аргументов true, а другой false |
not | Возвращает true если аргумент был false, и наоборот |
2 Кортежи и списки в Erlang (Tuples, Lists)
Кортежи
Кортежи встречаются не во многих языках. Это своего рода контейнеры, которые могут содержать другие типы. Их часто сравнивают со структурами в Си, только поля кортежа не имею имени. Несколько примеров:
И несколько функций, для работы с кортежами:
Списки
Если кортежи используются как структуры, то списки — основная структура данных в Erlang, с их помощью хранятся данные произвольной размерности. Да, в Erlang вы можете поместить в список элементы разных типов.
Более подробно про списки читайте в статьи «Обработка списков на Erlang«.
3 Erlang. Строки. Работа со списками
Strings (строки)
Символы в erlang представляются целыми числами, а строки — списками числовых значений ASCII символов. Это связано с тем что erlang пришел из телекома, а там, работа со строками это почти экзотика. Поскольку символы это ASCII, то в 32х разрядной версии для сравнения символа в памяти используется два байта, а в x64 — четыре, что не эффективно, но, насколько я понимаю, эта проблема решается в новых версиях. Разница между атомами, рассмотренными прежде, и строками в том что атомы можно только сравнивать (на манер констант), в то время как со строками можно производить всевозможные операции. Другая разница заключается в объеме памяти, занимаемой эрлангом атомом и строками, а также скорости операции сравнения, — атомы тут, безусловно выигрывают.
Цифровое представление символов можно получить добавив «$» перед символом:
Вот, что представляют из себя строки:
Обработка списков
Как уже упоминалось разница между кортежами и списками, в первую очередь, в том как они обрабатываются. Кортежи можно разбирать только поэлементно, с то время как списки можно разбивать на списки с «головой» и «хвостом», пока лист не будет пуст. Это очень популярная в эрланге операция и является чем-то типа аналога цикла.
[1, 2, 3] == [1 | [2, 3]] == [1 | [2 | [3]]] == [1 | [2 | [3 | []]]]
Вот еще хороший пример, иллюстрирующий то, как работает оператор «|»
И еще один, отлично иллюстрирующий идею:
Функции для работы со списками
4 Сопоставление с образцом (pattern matching) в Erlang
Сопоставление или pattern matching, это еще один способ присвоения, извлечения значений из списков или кортежей. Работает это так — сравнивается две переменные, если они не эквивалентны по типу или по значению возникнет исключение (ошибка). Если они равны, но отличаются только в переменных, то переменным в левой части выражения будет присвоено соответствующие значения справа. Проще всего это тоже пояснить на примере. Напомню, переменные всегда начинаются с заглавной:
А вот несколько примеров сопоставления которые не будут работать:
Знак подчеркивания можно использовать для игнорирования значений в левой части выражения:
Функции и модули в Erlang
Функции
Понятно что это такое, понятно для чего они используются. В erlang функции это все. Они вызываются, передаются в качестве аргументов, создаются другими функциями. Давайте определим, для примера, бесполезную функцию считающую произведение двух чисел:
В консоли такой синтаксис работать не будет, но можно изощрться создать через лямбда-функцию:
Классический пример факториала:
Как видите, сопоставление используется для аргументов функций, и при том очень широко:
В этом примере можно видеть, как в зависимости от значения константы в кортеже, выполняются разные типы вычислений.
Модули
Код нужно структурировать и организовывать, чтобы не потеряться в хаосе функций, для этого используются модули, — отдельные файлы содержащие своего рода «публичные» и «приватные» функции:
Операции if/then/else/case
Напишем несложную функцию, находящую максимальный элемент массива:
А вот пример того как работает case в erlang:
Примеры решения задач на списки на Erlang
Задача 1: Реализуйте функцию antimap(ListF, X), которая принимает список функций одного аргумента ListF и значение X, и возвращает список результатов применения всех функций из ListF к X.
В примере, список функций содержит лямбда-функции (анонимные/безымянные функции). Описание такой функции начинается ключевым словом fun и завершается end. Обе функции списка принимают один аргумент, при этом первая возвращает значение аргумента увеличенное на 2, а вторая — умноженное на 3.
Очевидно, наша функция должна завершать работу в случае, если список функций пуст, при этом результатом ее работы должен являться пустой список (первая строка листинг 1). В противном случае (если список функций не пуст) необходимо вернуть список, головой (первым элементом) которого является результат применения первой функции списка к аргументу, а хвостом (остальная часть списка) — результат рекурсивной обработки остальных элементов списка функций (вторая строка листинг 1).
рис. 1 пример использования функции antimap
Задача 2. Реализуйте функцию iterate(F, N), которая возвращает функцию, применяющую F к своему аргументу N раз (т.е.(iterate(F, 2))(X) == F(F(X)))
В первом примере в F3 помещается функция, умножающая аргумент на 2 пять раз, во втором — помещающая аргумент в кортеж 3 раза.
Особенность задачи в том, что требуется вернуть функцию, а не применять ее сразу к аргументу. Кроме того, обратим внимание не то, что в случае отрицательного N поведение функции не определено — в этом случае мы будем бросать исключение.
В первой строке листинг 2 обрабатывается случай, когда N рис. 2 пример работы iterate
В приведенном примере, в качестве первого аргумента group_by получает лямбда-функцию, возвращающую false если первый ее аргумент больше второго и true — в остальных случаях.
Исходный код функции group_by приведен на листинг 3.
Очевидно,функция (первый аргумент) должна вызываться для соседних элементов списка (второй аргумент).
Если исходный список пуст — результат работы group_by — пустой список (первая строка листинг 3).
Если исходный список содержит единственный элемент — в результате работы функции должен быть сформирован список, содержащий одну группу из одного элемента (вторая строка листинг 3).
В противном случае (если в списке более 1 элемента), необходимо вычислить функцию F для первых двух элементов списка, от полученного результата будет зависеть дальнейшая работа group_by. Может быть, нам хотелось бы использовать при этом уже знакомые нам ограничители (охраняющие выражения),однако, они не могут содержать вызовов функций, без которых нам не обойтись, поэтому в листинг 3 используется оператор ветвления.
Ветвления Erlang может реализовываться посредством следующих конструкций:
При выполнении конструкции if..end выполняется поиск ограничителя, который вернет true, а затем управление передается соответствующему выражению. Если ни один из ограничителей не вернул true — генерируется исключение.
В случае, если необходимо проверить истинность какого-либо выражения, удобнее использовать конструкцию case..of..end, при этом, сначала вычисляется выражение, а затем, результат вычисления последовательно сравнивается с шаблонами. Если какой-либо шаблон подошел — выполняется соответствующее ему выражение, в противном случае — генерируется исключение.
В нашей задаче удобнее использовать конструкцию case..of..end, т.к. необходимо проверять истинность выражения F(H1, H2) — четвертая строка листинг 3, однако, мы могли бы использовать и конструкцию if..end.
Независимо от того, что вернет F(H1, H2), наша функция должна рекурсивно обработать хвост списка ([H2|T]). При обработке хвоста будет получен промежуточный результат, к которому определенным образом необходимо прибавить элемент H1.
Если F(H1, H2) вернула true — то H1 должен быть добавлен к первому элементу-списку промежуточного результата (строка 7), иначе — из H1 формируется новая группа (новый список), которая добавляется в начало промежуточного результата (строка 8).
рис. 3 пример работы group_by
Задача 4. Реализуйте функцию iteratemap(F, X0, N), которая возвращает список длины N, состоящий из результатов последовательного применения F к X0.
Если N рис. 4 пример работы iteratemap
Литература:
Упражнения
В рамках программы изучения Erlang я делаю упражнения из книги «Erlang programming» Франческо Чезарини и Саймона Томпсона. Оказалось что это на редкость полезное занятие, — теперь синтаксис не кажется таким уж страшным, и решение хоть и простых, но практических задач, придает уверенности в себе. Ниже я приведу условия задачи (уж простите, без перевода) и свой вариант решения (зачастую не оптимальный).
Exercise 3-1: Evaluating Expressions
Write a function sum/1which, given a positive integer N, will return the sum of all the integers between 1 and N.
Write a function sum/2which, given two integers N and M, where N = M, you want your process to terminate
abnormally.
Exercise 3-2: Creating Lists
Write a function that returns a list of the format [1,2. N-1,N].
Example:
create(3) ⇒ [1,2,3].
Write a function that returns a list of the format [N, N-1. 2,1].
Exercise 3-3: Side Effects
Write a function that prints out the integers between 1 and N.
Hint: use io:format(«Number:
n»,[N]).
Write a function that prints out the even integers between 1 and N.
Hint: use guards.
Exercise 3-4: Database Handling Using Lists
Write a module db.erlthat creates a database and is able to store, retrieve, and delete
elements in it. The destroy/1function will delete the database. Considering that Erlang
has garbage collection, you do not need to do anything. Had the dbmodule stored
everything on file, however, you would delete the file. We are including the destroy
function to make the interface consistent. You may notuse the listslibrary module,
and you have to implement all the recursive functions yourself.
Hint: use lists and tuples as your main data structures. When testing your program,
remember that Erlang variables are single-assignment:
db:new() ⇒ Db.
db:destroy(Db) ⇒ ok.
db:write(Key, Element, Db) ⇒ NewDb.
db:delete(Key, Db) ⇒ NewDb.
db:read(Key, Db) ⇒
db:match(Element, Db) ⇒ erlang язык программирования обучение для чайников.
И мой вариант решения:
Другой вариант решения:
Упражнения на списки в Erlang
Exercise 3-5: Manipulating Lists
Write a function that, given a list of integers and an integer, will return all integers
smaller than or equal to that integer.
Example:
filter([1,2,3,4,5], 3) ⇒ [1,2,3].
Write a function that, given a list, will reverse the order of the elements.
Example:
reverse([1,2,3]) ⇒ [3,2,1].
Write a function that, given a list of lists, will concatenate them.
Example:
concatenate([[1,2,3], [], [4, five]]) ⇒ [1,2,3,4,five].
Hint: you will have to use a helpfunction and concatenate the lists in several steps.
Write a function that, given a list of nested lists, will return a flat list.
Hint: use concatenateto solve flatten
Мой вариант решения:
Упражнение по Erlang — Сортировка
Exercise 3-6: Sorting Lists
Implement the following sort algorithms over lists:
The head of the list is taken as the pivot; the list is then split according to those
elements smaller than the pivot and the rest. These two lists are then recursively
sorted by quicksort, and joined together, with the pivot between them.
Надо сказать, это очень инетересное упражнение и заставило прилично вникнуть и понять синтаксис и работу erlang. Допускаю, что решение не самое оптимальное, и можно написать вариант быстрее, но мне кажется, что код очень легко читается, воспринимается и позволяет сразу увидеть суть алгоритма:
PS. Текст статьи я в значительной части заимствовал у Шереметова, но авторские права, насколько я понимаю, соблюдены.
Начала работы с Erlang
Перевод: Михаил Купаев
Тайкало Олег
Дмитрий Димандт
Владислав Чистяков
Источник: Getting Started With Erlang
Материал предоставил: RSDN Magazine #3-2006
Опубликовано: 06.12.2006
Исправлено: 13.12.2006
Версия текста: 1.0
По собственному опыту знаю – увидев в заголовке или в тексте статьи незнакомые названия экзотических языков или труднопроизносимые термины, читатель стремится перелистнуть страницу в поисках чего-нибудь поинтереснее. В этой статье мы постараемся убедить вас, что как минимум один такой экзотический язык может быть интересным.
Так что же такое Erlang?
В далеком 1985-м году группа разработчиков из компьютерных лабораторий компании Ericsson решила создать язык, который идеально бы подходил для решения задач в телекоме. Шесть лет спустя, в 1991-м, миру был представлен такой язык – Erlang.
ПРИМЕЧАНИЕ Среди любителей Erlang-а бытует мнение, что Erlang расшифровывается как ERicsson LANGuage. На самом деле язык назван в честь Агнера Крарупа Эрланга, датского математика, который работал в области телекоммуникаций. Так, единица измерения телекоммуникационного траффика также называется «Erlang». С 1992 года Erlang начал применяться в компании Ericsson для разработки телекомуникационного оборудования. Например, бoльшая часть функциональности флагманского продукта компании, свитча AXD-301, реализована с использованием Erlang-а. В 1998 году были опубликованы исходные коды языка и его библиотек. С тех пор Erlang стал не просто языком для телекоммуникационных приложений, а полноценным языком общего назначения. Ericsson до сих пор развивает Erlang, и его бесплатная версия собирается из тех же исходников, что и коммерческая. И все же, что такое Erlang?Кратко говоря, Erlang – это язык программирования общего назначения и среда исполнения. В язык встроена поддержка распределенных и параллельных вычислений. А если говорить подробнее, то Erlang предлагает разработчику следующее В состав стандартных библиотек Erlang-а входят, например, следующие продукты: И где я могу его использовать?Erlang можно использовать в самых разнообразных областях. На данный момент Erlang с успехом применяется, например: 1 Введение1.1 ВступлениеЭто – простая обучающая статья, посвященная началам работы с Erlang. В ней все верно, но это только часть правды. Например, я привожу только самую простую форму синтаксиса, но не все его эзотерические формы. Там, где я уж очень упрощаю, я буду писать *manual*, что означает, что куда больше информации можно найти в книгах или в Erlang Reference Manual. Я также предполагаю, что вы не в первый раз сели за компьютер, и что вы имеете общее представление о том, как их программируют. Не волнуйтесь, я не рассчитываю на то, что вы – гуру. 1.2 Пропущенные вещиПеречисленное ниже в этой статье не рассматривается: Здесь не рассматривается также связь с внешним миром и/или ПО, написанным на других языках. Впрочем, для этого есть отдельная обучающая статья, Interoperability Tutorial. 2 Последовательное программирование2.1. Оболочка ErlangВ большинстве ОС есть командный интерпретатор или оболочка. В Unix и Linux их масса, а в Windows есть Command Prompt. У Erlang есть собственная оболочка, в которой можно напрямую писать куски Erlang-кода, исполнять его и смотреть, что получается (*manual*). Запустите оболочку Erlang (в Linux или UNIX), введя в командной строке erl (или просто выберите Erlang в меню запуска в Windows). Вы увидите следующее: Теперь введите «2 + 5.», как показано ниже: Как видите, оболочка Erlang пронумеровала вводимые строки (как 1>, 2> и т.д.), и совершенно корректно ответила, что 2 + 5 равно 7! Также заметьте, что ей нужно указать окончание ввода, поставив точку и нажав Enter. В случае ошибки можно воспользоваться клавишей backspace, как и в большинстве других оболочек. В оболочке есть множество других команд редактирования (*manual*).
|