erlang язык программирования обучение для чайников

Erlang для самых маленьких. Глава 3: Базовый синтаксис функций

erlang язык программирования обучение для чайников. Смотреть фото erlang язык программирования обучение для чайников. Смотреть картинку erlang язык программирования обучение для чайников. Картинка про erlang язык программирования обучение для чайников. Фото erlang язык программирования обучение для чайниковДоброй ночи, Хабр! Мы продолжаем изучение 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).

erlang язык программирования обучение для чайников. Смотреть фото erlang язык программирования обучение для чайников. Смотреть картинку erlang язык программирования обучение для чайников. Картинка про erlang язык программирования обучение для чайников. Фото erlang язык программирования обучение для чайниковрис. 1 пример использования функции antimap

Задача 2. Реализуйте функцию iterate(F, N), которая возвращает функцию, применяющую F к своему аргументу N раз (т.е.(iterate(F, 2))(X) == F(F(X)))

В первом примере в F3 помещается функция, умножающая аргумент на 2 пять раз, во втором — помещающая аргумент в кортеж 3 раза.

Особенность задачи в том, что требуется вернуть функцию, а не применять ее сразу к аргументу. Кроме того, обратим внимание не то, что в случае отрицательного N поведение функции не определено — в этом случае мы будем бросать исключение.

В первой строке листинг 2 обрабатывается случай, когда N erlang язык программирования обучение для чайников. Смотреть фото erlang язык программирования обучение для чайников. Смотреть картинку erlang язык программирования обучение для чайников. Картинка про erlang язык программирования обучение для чайников. Фото erlang язык программирования обучение для чайниковрис. 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).

erlang язык программирования обучение для чайников. Смотреть фото erlang язык программирования обучение для чайников. Смотреть картинку erlang язык программирования обучение для чайников. Картинка про erlang язык программирования обучение для чайников. Фото erlang язык программирования обучение для чайниковрис. 3 пример работы group_by

Задача 4. Реализуйте функцию iteratemap(F, X0, N), которая возвращает список длины N, состоящий из результатов последовательного применения F к X0.

Если N erlang язык программирования обучение для чайников. Смотреть фото erlang язык программирования обучение для чайников. Смотреть картинку erlang язык программирования обучение для чайников. Картинка про erlang язык программирования обучение для чайников. Фото erlang язык программирования обучение для чайниковрис. 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 язык программирования обучение для чайников. Смотреть фото erlang язык программирования обучение для чайников. Смотреть картинку erlang язык программирования обучение для чайников. Картинка про erlang язык программирования обучение для чайников. Фото erlang язык программирования обучение для чайников

По собственному опыту знаю – увидев в заголовке или в тексте статьи незнакомые названия экзотических языков или труднопроизносимые термины, читатель стремится перелистнуть страницу в поисках чего-нибудь поинтереснее. В этой статье мы постараемся убедить вас, что как минимум один такой экзотический язык может быть интересным.

Так что же такое 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*).

ПРИМЕЧАНИЕ

В этой статье вы найдете много выданных оболочкой номеров строк, идущих не по порядку, поскольку статья и код писались в несколько приемов.

Теперь посмотрите на более сложный расчет:

Здесь вы видите использование скобок, а также оператора умножения «*» и оператора деления «/», как в обычной арифметике (*manual*).

Чтобы закрыть Erlang-систему и оболочку Erlang, нажмите Control-C. Вы увидите следующее:

Нажмите «a» для выхода из оболочки Erlang.

Еще один способ выхода из Erlang – ввести halt():

Примечание: Под Windows Control-C работает только в консольной версии. Зато Control-Break работает везде.

2.2 Модули и функции


2.2 Modules and Functions

От языка программирования немного толку, если он позволяет только вводить строки в оболочке. Поэтому займемся небольшой Erlang-программой. Поместим ее в файл с именем tut.erl (имя файла tut.erl имеет значение, убедитесь также, что файл находится в том каталоге, откуда вы запускали erl (*manual*)), используя какой-нибудь текстовый редактор. Если вам повезет, у вашего редактора будет Erlang-режим, помогающий вводить и форматировать код (*manual*), но прекрасно можно обойтись и без этого. Вот код, который нужно ввести:

Нетрудно понять, что эта «программа» удваивает значения чисел. К первым двум строчкам я вернусь позже. Скомпилируем программу. Это можно сделать из оболочки Erlang:

Надпись «» означает, что все нормально скомпилировалось. Если появилось «error», значит, вы ошиблись при вводе текста. При этом появятся сообщения об ошибках, дающие представление о том, что пошло не так, как надо. Исправьте текст и попробуйте еще раз.

Теперь запустим программу.

Результатом удвоения 10, как и ожидалось, является 20.

Теперь вернемся к первым двум строкам. Erlang-программы хранятся в файлах. Каждый файл содержит то, что мы называем Erlang-модулем. Первая строка кода модуля сообщает имя модуля (*manual*).

означает вызов функции double из модуля tut с аргументом 10.

говорит, что модуль tut содержит функцию с именем double, принимающую один аргумент (в нашем примере х) и что эту функцию можно вызвать извне модуля tut. Подробнее об этом будет сказано ниже. Еще раз обратите внимание на точку в конце строки.

Перейдем к более сложному примеру, факториалу числа (факториал 4, например, равен 4 * 3 * 2 * 1). Введите следующий код в файл tut1.erl.

А теперь рассчитайте факториал 4.

указывает, что факториал 1 равен 1. Заметьте, что эта часть завершается точкой с запятой «;», что показывает, что функция еще не окончена. Вторая часть:

У функции может быть много аргументов. Давайте расширим модуль tut1 довольно тупой функцией, перемножающей два числа:

В приведенном примере числа – это целые, а аргументы функций N, X, Y – это переменные. Переменные должны начинаться с заглавных букв (*manual*). Примерами переменных могут быть Number, ShoeSize и т.д.

2.3 Атомы

Атомы – еще один тип данных в Erlang. Атомы начинаются с маленькой буквы (*manual*), например: charles, centimeter, inch. Атомы – это просто имена и ничего больше. Они не похожи на переменные, которые могут иметь значение.

Введите следующую программу (в файл tut2.erl), которая может пригодиться при конвертации дюймов в сантиметры и наоборот:

Компилируем и запускаем:

Заметьте, что я ввел числа с плавающей точкой без всяких объяснений, но думаю, вы с этим справитесь.

Посмотрите, что произойдет, если я подсуну этой функции что-либо отличное от дюймов или сантиметров:

Две части функции convert называются выражениями. Очевидно, «miles» не является частью ни одного из выражений. Erlang-система не может сопоставить с ним никакого выражения, и выдает сообщение об ошибке function_clause. Приведенный результат выглядит страшненько, но при небольшой практике вы научитесь точно видеть, в каком месте кода случилась ошибка.

2.4 Кортежи

Программу tut2 трудно отнести к хорошему стилю программирования. Посмотрите:

Это должно значить, что 3 – это дюймы? Или что 3 – это сантиметры, и мы хотим сконвертировать их в дюймы? В Erlang есть способ группировать сущности, чтобы сделать их понятнее. Мы называем это кортежами (tuples). Кортежи заключаются в фигурные скобки («<" и ">«).

Мы можем записать 3 дюйма как , а 5 сантиметров как . Напишем новую программу, конвертирующую сантиметры в дюймы и наоборот (файл tut3.erl).

Компилируем и проверяем:

Обратите внимание – в строке 16 мы конвертируем 5 дюймов в сантиметры и обратно, при этом мы получаем исходное значение, что обнадеживает. Это значит, что аргументы функции могут быть результатами другой функции. Остановимся и разберемся, как работает строка 16. Аргумент, переданный функции , сперва сопоставляется с заголовком первого выражения convert_length, т.е. convert_length(), и выясняется, что не соответствует (заголовок – это часть, стоящая перед «->»). После этой неудачи мы пробуем заголовок следующего выражения, convert_length(), который подходит, и Y получает значение 5.

2.5 Списки

Несмотря на то, что кортежи группируют значения, нам требуется еще и возможность составлять списки значений. Списки в Erlang заключаются в квадратные скобки («[» и «]»). Например, список температур в различных городах мира может выглядеть так:

Заметьте, что этот список слишком длинный, чтобы уместиться на одной строчке. Это не имеет значения, Erlang позволяет переносить строки в любых «осмысленных местах», но не в середине атомов, целых чисел и т.д.

Очень полезным является использование оператора «|» при декомпозиции списков. Лучше всего показать это на примере, используя оболочку.

ПРИМЕЧАНИЕ

Erlang, как и большинство функциональных языков, для работы со списками использует специальную структуру данных, имеющую мощную поддержку в языке. Эта структура – ни что иное, как обыкновенный однонаправленный связанный список, который можно создавать и читать, но нельзя изменять (запрещается изменение не только самого списка, но и значений, в нем хранящихся). Единственный способ изменить список в функциональных языках – создать новый список. Однако сам факт неизменности созданного списка позволяет порождать новые списки, присоединяя их к новым (головным) элементам. По этой причине новые элементы всегда добавляются в начало списка. – прим. ред.

Здесь показано использование | для получения первых двух элементов списка. Конечно, если попробовать получить больше элементов, чем есть в списке, мы получим ошибку. Обратите внимание, что в примере ниже для представления пустого списка используется «[]»:

Во всех приведенных примерах я использовал новые имена переменных, вместо повторного использования старых: First, TheRest, E1, E2, R, A, B, C. Причина в том, что переменной можно присвоить значение в ее контексте (области видимости) лишь однажды. Я вернусь к этому позже, это не так страшно, как кажется!

Следующий пример показывает, как определить длину списка:

Компилируем и проверяем (файл tut4.erl):

Длина пустого списка, очевидно, равна 0.

Длина списка с первым элементом First и остальными элементами Rest равна 1 + длина Rest.

ПРИМЕЧАНИЕ

Только для подготовленных читателей: Это не хвостовая рекурсия, есть лучший способ написать эту функцию.

В общем, мы можем сказать, что кортежи используются там, где в других языках используются «записи» или «структуры», а списки там, где нужно представить сущности переменной длины (то есть там, где в других языке используются связанные списки).

В Erlang нет строкового типа данных. Вместо строк можно использовать списки ASCII-символов. Так, список [97, 98, 99] эквивалентен «abc». Оболочка Erlang «умная», и, догадываясь, какого сорта список мы имеем в виду, выдает его в наиболее подходящем по ее мнению виде. Например:

2.6 Стандартные модули и документация

В Erlang множество стандартных модулей, предназначенных для упрощения работы. Например, модуль io содержит множество функций, помогающих при форматированном вводе/выводе. Чтобы получить информацию о стандартных модулях, введите в командной строке команду erl –man. Например:

Если на вашей системе это не работает, воспользуйтесь документацией в виде HTML, включенной в Erlang/OTP, или скачайте ее в HTML- или в PDF-виде с сайтов www.erlang.se (коммерческий Erlang) или www.erlang.org (open source), Например, для версии R9B:

2.7 Вывод на консоль

Неплохо бы привести пример форматированного вывода. Итак, следующий пример показывает простой способ использования функции io:format. Конечно, так же, как и все другие экспортируемые функции, работу функции io:format можно протестировать в оболочке:

Функция format/2 (т.е. format с двумя аргументами) принимает два списка. Первый список практически всегда заключен в кавычки. Этот список выводится как есть, за тем исключением, что каждое

w заменяется элементами, которые берутся по порядку из второго списка. Каждое

n заменяется новой строкой. Сама функция io:format/2, если все идет по плану, возвращает атом ОК. Как и другие функции Erlang, при ошибке она «вылетает». Это не недостаток Erlang, а продуманная политика. В Erlang есть сложная система обработки ошибок, о которой речь пойдет ниже. В качестве упражнения попробуйте заставить io:format «вылететь», это несложно. Но обратите внимание, что io:format вылетает, а сама оболочка – нет.

2.8 Больший пример

Теперь разберем больший пример, чтобы закрепить пройденное. Предположим, у нас есть список температурных наблюдений для различных городов мира. Некоторые из них – в градусах по Цельсию, некоторые – по Фаренгейту (как было показано выше). Сперва сконвертируем их все в градусы Цельсия, а затем выведем данные:

Прежде чем посмотреть, как работает эта программа, обратите внимание на комментарии, появившиеся в коде. Комментарий начинается со знака % и продолжается до конца строки. Заметьте также, что строка –export([format_temps/1]). включает только функцию format_temps/1, остальные функции являются локальными, то есть невидимыми извне модуля tut5.

Заметьте также, что при тестировании программы в оболочке, я разнес входные данные на две строки, так как строка получалась слишком длинной.

print_temp просто вызывает io:format аналогично тому, что описывалось выше.

-15w говорит выводить текст с шириной поля 15 и выравнивать его влево (*manual*).

Теперь мы вызываем format_temps(Rest) с остатком списка в качестве аргумента. Это похоже на конструкции циклов в других языках программирования (да, это рекурсия, но пусть вас это не волнует). Так что та же функция format_temps вызывается снова, на этот раз со значением City, равным >, и повторяется описанная выше процедура. Все это продолжается, пока список не опустеет (т.е. станет равным []), чему соответствует первое выражение, format_temps([]). Оно просто возвращает атом «ok», и исполнение программы заканчивается.

2.9 Сопоставление, защита и область видимости переменных

В списке, подобном приведенному выше, может потребоваться найти максимальную и минимальную температуру. До расширения программы рассмотрим функции для поиска максимального значения элемента списка:

Прежде всего, заметьте, что здесь есть две функции с одинаковым названием list_max. Они принимают разное количество аргументов (параметров). В Erlang это означает совершенно разные функции. Если нужно различать эти функции, можно написать name/arity, где name – имя функции, а arity – число аргументов, в данном случае list_max/1 и list_max/2.

spawn возвращает идентификатор процесса (process identifier, pid), уникально идентифицирующий процесс. – это pid вызова функции spawn, приведенного выше. В следующем примере будет показано, как использовать pid.

Заметьте также, что мы использовали

w в io:format. Процитирую документацию: «

p записывает данные со стандартным синтаксисом так же, как

w, но переносит части термов, если их текстовое представление превышает длину строки, на несколько строк и осмысленно выравнивает их. Она также пытается определить список используемых символов и выводить их в строковом виде».

3.2 Обмен сообщениями

В следующем примере создаются два процесса, несколько раз отправляющие друг другу сообщения:

Функция start сперва создает процесс, назовем его «pong»:

Этот процесс исполняет tut15:pong(). Pong_PID – идентификатор процесса «pong». Теперь функция start создает другой процесс, «ping»:

Этот процесс выполняет:

Теперь процесс «pong» выполняет:

Чтобы позволить процессу ожидать сообщения от других процессов, используется конструкция receive. Ее формат:

Заметьте: перед end нет никакого «;».

Сообщения Erlang-процессов – это просто корректные Erlang-термы. Это могут быть списки, кортежи, целые, атомы, pid и т.д.

У каждого процесса есть очередь входящих сообщений. При получении новых сообщений они помещаются в конец очереди. При исполнении процессом конструкции receive первое сообщение в очереди сопоставляется с первым шаблоном в receive, в случае соответствия сообщение удаляется из очереди, и выполняется действие, соответствующее шаблону.

Если первый шаблон не подходит, проверяется второй, за ним третий, и так далее, пока не будут проверены все. Если шаблонов не осталось, первое сообщение оставляется в очереди, и берется второе сообщение. Если оно соответствует какому-либо шаблону, выполняется соответствующее шаблону действие, а сообщение удаляется из очереди (при этом первое и все остальные сообщения остаются в очереди). Если соответствий второму сообщению также не найдено, берется третье, и так до конца очереди. Если достигнут конец очереди, процесс блокируется (останавливает исполнение) и ждет получения новых сообщений и повторения процесса.

Конечно, реализация Erlang «умна», и минимизирует число проверок сообщений в каждом receive.

Вернемся к примеру программы «Пинг-понг».

«Pong» ожидает сообщений. При получении атома finished «pong» выводит «Понг завершил работу», и поскольку ему ничего другого не остается, завершается. Если он получает сообщение формата:

он пишет «Понг получил пинг» и посылает процессу «ping» атом pong:

Обратите внимание на то, что для отправки сообщений используется оператор «!». Синтаксис «!»:

Message (Erlang-терм) посылается процессу с идентификатором Pid.

После отправки сообщения pong процессу «ping», «pong» снова вызывает функцию pong, что заставляет его вернуться к receive и ждать следующего сообщения. Теперь посмотрим на процесс «ping». Как вы помните, он начинается с исполнения:

В функции ping/2 мы видим, что второе выражение ping/2 исполняется, так как значение первого параметра – 3.

Второе выражение посылает сообщение процессу «pong»:

self() возвращает pid процесса, исполняющего self(), в данном случае pid процесса «ping» (вспомните код «pong», этот pid попадет в переменную Ping_PID в обсуждавшемся выше receive).

Теперь «ping» ждет ответа от «pong»:

и выводит «Пинг получил понг» при получении ответа, после чего снова вызывает функцию ping.

N-1 вызывает уменьшение первого аргумента, пока он не станет равным 0. После этого выполняется первое выражение ping/2:

Процессу «pong» посылается атом finished (вызывая его завершение), и выводится «Пинг завершил работу». После этого «ping» тоже завершается, поскольку делать ему больше нечего.

3.3 Регистрированные имена процессов

В приведенном выше примере мы сперва создали «pong», чтобы иметь возможность задать идентификатор «pong» при запуске «ping». Дело в том, что «ping» должен каким-то образом узнать идентификатор «pong», чтобы смочь передать ему сообщение. Иногда процессы, которые должны знать идентификаторы друг друга, запускаются совершенно независимо друг от друга. Поэтому Erlang предоставляет механизм именования процессов, и эти имена можно использовать как идентификаторы вместо pid. Это делается с помощью BIF register:

Перепишем пример «Пинг-понг», используя эту функцию, и присвоим процессу «pong» имя pong:

В функции start/0 процесс «pong» запускается и получает имя pong:

Процесс «ping» теперь может отправлять сообщения pong так:

и ping/2 превращается в ping/1, поскольку аргумент Pong_PID больше не нужен.

3.4 Распределенное программирование

При запуске Erlang-системы, собирающейся общаться с другими Erlang-системами, ей нужно дать имя, например:

Подробнее об этом будет сказано ниже (*manual*). Если вы хотите поэкспериментировать с распределенным Erlang, но у вас только один компьютер, можно запустить две отдельных Erlang-системы на одном компьютере, дав им разные имена. Каждая Erlang-система, работающая на компьютере, называется Erlang-узлом (node).

Примечание: erl –sname предполагает, что все узлы находятся в одном IP-домене, и можно использовать только первый компонент IP-адреса. Если мы хотим использовать узлы из разных доменов, нужно использовать –name, но тогда все IP-адреса нужно приводить полностью (*manual*).

Вот пример «Пинг-понг», измененный для запуска на двух разных узлах:

Предположим, у нас есть два компьютера с именами gollum и kosken. Запустим сначала узел ping на kosken, а затем – узел pong на gollum.

На kosken (под Linux/Unix):

Теперь запустим на gollum процесс «pong»:

и процесс «ping» на kosken (из приведенного выше кода видно, что параметр функции start_ping – это имя узла Erlang-системы, где запускается «pong»):

Итак, программа «Пинг-понг» работает, на стороне «pong» мы видим:

В коде tut17 мы видим, что сама функция pong не изменилась, строки:

работают по-прежнему, независимо от того, на каком узле исполняется процесс «ping». Таким образом, pid в Erlang содержит информацию о месте исполнения процесса, и если вам известен pid процесса, «!» можно использовать для отправки ему сообщения независимо от узла, где он исполняется.

Отличие в том, как производится отправка сообщений регистрированному процессу на другом узле:

Используется кортеж вместо обычного registered_name.

В предыдущем примере мы запускали «ping» и «pong» из оболочек на двух разных Erlang-узлах. spawn также можно использовать для запуска процессов на других узлах. Следующий пример – это опять программа «Пинг-понг», но на этот раз мы запустим «ping» на другом узле:

Предполагая, что Erlang-система ping (но не процесс «ping») уже запущен на kosken, выполним на gollum:

Заметьте, что теперь все выводится на gollum, так как система ввода/вывода выясняет, откуда был запущен процесс, и отправляет весь вывод туда.

3.5 Больший пример

Перейдем к большему примеру. Создадим предельно простой «messenger». Это программа, позволяющая пользователям зарегистрироваться на разных узлах и обмениваться сообщениями.

Перед началом нужно сказать следующее:

Наш messenger позволит «клиентам» подключаться к центральному серверу и сообщать, кто они и где они. Т.е. пользователю не нужно знать имя Erlang-узла другого пользователя, чтобы отправить сообщение.

Файл messenger.erl:

Чтобы использовать эту программу, нужно:

В примере использования этой программы я запустил узлы на четырех разных компьютерах. Если в вашей сети нет такого количества свободных машин, запустите несколько узлов на одном компьютере.

Запустим четыре Erlang-узла, messenger@super, c1@bilbo, c2@kosken, c3@gollum.

Сперва запускаем сервер на messenger@super:

Теперь в систему входит Peter на узле c1@bilbo:

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *