Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Languages
 С
 GNU С Library 
 Qt 
 STL 
 Threads 
 C++ 
 Samples 
 stanford.edu 
 ANSI C
 Libs
 LD
 Socket
 Pusher
 Pipes
 Encryption
 Plugin
 Inter-Process
 Errors
 Deep C Secrets
 C + UNIX
 Linked Lists / Trees
 Asm
 Perl
 Python
 Shell
 Erlang
 Go
 Rust
 Алгоритмы
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 Secure Programming for Li...6507 
 Linux Kernel 2.6...5279 
 Trees...1118 
 Максвелл 3...1050 
 William Gropp...987 
 Go Web ...961 
 Ethreal 3...929 
 Ethreal 4...915 
 Gary V.Vaughan-> Libtool...913 
 Ext4 FS...901 
 Clickhouse...900 
 Rodriguez 6...899 
 Ethreal 1...896 
 Steve Pate 1...884 
 C++ Patterns 3...860 
 Assembler...853 
 Ulrich Drepper...844 
 DevFS...786 
 MySQL & PosgreSQL...771 
 Стивенс 9...757 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

Введение в Erlang

Эрланг обычно входит в стандартный список пакетов наиболее популярных дистрибутивов. Для пакетной установки эрланга в зависимости от вашего дистрибутива нужно набрать что-то типа
  yum install erlang
или
  zypper install erlang
Можно собрать и поставить эрланг из исходников - смотрите http://www.erlang.org/doc/installation_guide/INSTALL.html

Итак, эрланг установлен, запускаем в командной строке интерпретатор erl

 $ erl
 Erlang R15B03 (erts-5.9.3) [source] [smp:2:2] 
 
 Eshell V5.9.3  (abort with ^G)
 1> 

Базовые типы

Набираем комментарий и арифметическое выражение:
 1> % вычислим сумму двух чисел
 1> 20+30.
 50
 2> 
Обратите внимание, что после суммы мы ставим точку, это говорит интерпретатору, что команда закончена, и нажав enter, мы получаем ответ - 50. Можно проверить, как работает арифметика больших чисел:
 2> 1234567878 * 345345345345345345.
 426352270180180179753827910
Числа с основанием, отличным от 10, записываются в следующем виде: Основание#3начение. Основание является целым числом из интервала от 2 до 16, а значение — числом, имеющим заданное основание. К примеру, 2#1010 означает число 10 с основанием 2 и - 1б#ЕА означает число -234 с основанием 16, поскольку буквы от А до F кодируют числа от 10 до 15 для чисел с основанием 16.

В Erlang действительные числа (floats) представлены типом Float, например:

  1.234Е-10
Запись Е-10 означает, что точка смещена на 10 разрядов влево. Так, выражение - 1 01.234Е-10 является записью числа 1.234 • Ю или 0.0000000001234. Точность действительных чисел представлена в виде 64 битов и соответствует стандарту IEEE 754-1985.

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

 > My_var = 1.
 1
Если мы попробуем этой переменной присвоить другое значение - получим ошибку:
 > My_var = 2. 
 ** exception error: no match of right hand side value 2
Здесь нас ждет шок: переменные в эрланге не изменяются, их можно проинициализировать всего один раз. Авторы языка говорят, что это дисциплинирует мышление. Во всех языках программирования оператор = означает присваивание, а в эрланге это не так - здесь это сопоставление по образцу. В эрланге переменная - это указатель на область памяти, которую нельзя изменить. В эрланге это сделано для того, чтобы не было проблем с разделяемой памятью и блокировками, которые есть в сях или жабе.

В Erlang вызов переменных является вызовом по значению (call by value): все аргументы функции вычисляются перед выполнением тела функции. Вызов по ссылке невозможен.

Erlang является языком с динамической типизацией (dynamic type system). Типы определяются во время исполнения, также как и правомерность применения операций к переменным.

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

В текущей реализации ВМ Erlang используется копирующий, последовательный сборщик мусора. Сборка мусора проводится отдельно для каждого запущенного процесса: если одному из процессов не хватает памяти, запускается сборщик мусора.

Теперь перейдем к атомам. Они служат тем же целям, что и тип перечисление. Если взять например си, то в нем константы записываются так:

 #define OP_READ 1
 #define OP_WRITE 2
 #define OP_SEEK 3
В эрланге все атомы начинаются с прописной буквы - это основное отличие от переменных. Атомы можно заключить в одинарные кавычки. В Erlang не выделено отдельного типа для логических значений (booleans). Вместо этого совместно с операциями сравнения используются атомы true и false.
 > not((l < 3) and (2==2)).
 false
Логические операторы:
 and
 andalso
 or
 orelse
 xor
 not.
Операторы сравнения:
 ==   Равно
 /=   Не равно
 =:=  В точности равно
 =/=  В точности не равно
 =<   Меньше либо равно
 <    Меньше
 >=   Больше либо равно 
 >    Больше

Кортежи

Кортеж (tuple) - это коллекция фиксированного размера, данные внутри одного кортежа могут быть произвольного типа. Кортеж заключен в фигурные скобки:
 > {f1, 2}.
Данный кортеж состоит из атома и целого числа. Кортежи похожи на сишные структуры, только анонимные. Например, в си можно создать структуру точка и потом ее проинициализировать с помощью оператора точка: Если на первом месте кортежа стоит атом, то этот элемент называют тегом (tag).
 struct point 
 {
   int x;
   int y;
 } P;
 
 P.x = 10; P.y = 45;
В эрланге нет оператора точка. Ту же структуру, только сразу проинициализированную, в эрланге можно написать с помощью кортежа так:
 >  P = {10, 45}
Но лучше для удобства записать так - здесь атом фактически является именем структуры - так рекомендовано отцами языка :-)
 >  P = {point, 10, 45}
Чтобы извлечь данные из этой структуры в переменные X и Y:
 >  {point, X, Y} = Point.
Если у вас имеется сложный кортеж и вы хотите извлечь из него данные, то вы можете написать кортеж такой же формы (структуры) как и ваш, но в тех местах откуда вы хотите извлечь данные поместите несвязанные переменные. (Этот метод извлечения данных путем сопоставления по образцу называется унификацией и используется во множестве функциональных и логических языков программирования.)

Кортежи могут быть вложенными:

 > Person = {person,
     {name, joe},
     {height, 1.82},
     {footsize, 42},
     {eyecolour, brown}}.
Обращаю внимание, что для цвета глаз используются атомы как для задания имени поля, так и для его значения. Как извлечь отдельное поле из какой-то сложной структуры ? Сначала проинициализируем сложную структуру:
 > Person={person,{name,{first,joe},{last,armstrong}},{footsize,42}}.
После чего напишем образец для инициализации отдельного поля:
 > {_,{_,{_,Who},_},_} = Person.
Здесь символ подчеркивания называется анонимной переменной.

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

 1> tuple_size({abc, {def, 123}, ghi}).
 3
 2> element(2,{abc, {def, 123}, ghi}).
 {def,123}
 3> setelement(2, {abc, {def, 123}}, def).
 {abcdef}
 4> {1,2}<{1,3}.
 true
Кортежи индексируются с 1, а не с 0.

Списки

Списки, как и кортежи, могут хранить обьекты произвольного типа. Списки - коллекции, заключенные в квадратные скобки:
 > ThingsToBuy = [{apples,10},{pears,6},{milk,3}].
 > [1+7,hello,2-2,{cost, apple, 30-20},3].
Первый элемент списка называется головой списка, все остальное - хвост. Голову списка можно отделить от хвоста вертикальной чертой:
 > [1 | 2].
Для списка [1,2,3] 1 будет являться головой списка, а [2,3] — хвостом, этот список может быть записан в виде [ 1 | [2,3]]. Выполнив аналогичную операцию с хвостом списка, получим [1| [2| [3]]] и далее [ 1 | [ 2 | [ 3 | [ ] ] ] ] . Этот список мож­ но записать ещё одним способом [ 1 , 2 | [ 3 | [ ] ] ] , перед применением конструк­ тора списков могут следовать несколько элементов, разделённых запятыми. Все эти списки эквивалентны исходному списку [1,2,3]. Список, последний хвост которого равен пустому списку, называют регулярным (proper list) или правильно построенным (well-formed list). В следующем примере все списки эквивалентны:
 [one, two, three, four]
 [one, two, three, f o u r | [ ] ]
 [one, two|[three, f o u r] ]
 [one, t w o | [ t h r e e | [ f o u r | [ ] ] ]
 [one|[two|[three|[four| [ ] ] ] ] ]
Как и из всего остального, мы можем извлекать элементы из списка с помощью оператора сопоставления по образцу. Если у нас имеется не пустой список L , тогда выражение [X|Y]=L , где Х и У - это несвязанные переменные, поместит голову списка в Х, а хвост списка - в У.
В кортеже нет возможности отделить голову.

У списков есть встроенные функции, которые нужно вызывать с префиксом lists :

 > lists:max([1,2,3]).
 > lists:reverse([1,2,3]).
 > lists:sort([2,l,3]).
 > lists:split(2,[3,4,10,7,9]).
 > lists:zip([l,2,3],[5.6,7]).
 > lists:delete(2,[l,2,3,2,4,2]).
 > lists:last([l,2,3]).
 > lists:member(5,[1,24]).
 > lists:nth(2.[3,4,16,7,9]).
 > length([l,2,3]).
Списки можно складывать:
 > [l,2,3]++[4,5,6].
 [1,2,3,4,5,6]
Списки можно вычитать:
 > [l,2,2,3,4,4]--[2,4].
 [1,2,3,4]
Прибавить элемент к списку можно двумя способами - в голову с посощью конструктора:
 > [1 | [2,3,4]].
и с помощью сложения:
 > [1] ++ [2,3,4].
Конструктор быстрее, чем сложение.

Строки в эрланге являются частным случаем списков.

 > [65,66,67].
 "ABC"
Строку можно проинициализировать с помощью двойных кавычек :
 > Str = "asdfsdf".
Две строки можно сложить так:
 > S1="123". 
 "123"
 > S2="456".
 "456"
 > S3 = S1 ++ S2.
 "123456"
Но это медленно. Быстрее так:
 > S3 = string:concat("123","456").
У строк отсутствуют встроенные функции. Любителям императивных языков тут будет неуютно.

Оператор for

В эрланге нет стандартного цикла for, нам прийдется изобретать его :-) Напишем свою функцию for, который используем для создания списка чисел от 1 до 10. Создадим файл my_func.erl следующего содержания:
 -module(my_func).
 -export([for/3]).
 
 for(Max, Max, F) -> [F(Max)];
 for(I, Max, F)   -> [F(I)|for(I+1, Max, F)].
Здесь используется все тот же эрланговский алгоритм разбиения списка на голову и хвост. В первой итерации это будет [F(1)|for(2,10,F)], во второй - [F(2)|for(3,10,F)], и т.д. Запускаем интерпретатор:
 > c(my_func).
 > my_func:for(1,10,fun(I) -> I end).
 [1,2,3,4,5,6,7,8,9,10]
Теперь напишем функцию sum, вычисляющую сумму элементов списка: Добавляем в файл my_func.erl код:
 -export([sum/1]).
 
 sum([H|T]) -> H + sum(T);
 sum([]) -> 0.
Запускаем интерпретатор:
 > c(my_func).
 > L = [1,2,3,4,5].
 > my_func:sum(L).
 15
Здесь мы используем все тот же алгоритм отсечения головы от хвоста.

Теперь напишем функцию map, которая удваивает все элементы списка. Добавляем в файл my_func.erl код:

 -export([map/2]).
 
 map(_, [])     -> [];               
 map(F,  [H|T]) -> [F(H)|map(F, T)]. 
 
 
Запускаем интерпретатор:
 > c(my_func).
 > L = [1,2,3,4,5].  
 [1,2,3,4,5]
 > my_func:map(fun(X) -> 2*X end, L).
 [2,4,6,8,10]

Обработчики списков

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

Так, удвоить каждый элемент списка можно вот таким обработчиком:

 > [2*X || X <- L ].
То, что стоит слева от двойной вертикальной черты - это паттерн, а справа - конструктор.

Реализуем сортировку списка: добавим в файл my_func.erl код:

 -export([qsort/1]).
 
 qsort([]) -> [];
 qsort([Pivot|T]) ->
 qsort([X || X <- T, X < Pivot])
 ++ [Pivot] ++
 qsort([X || X <- T, X >= Pivot]).
Здесь ++ - это инфиксный оператор добавления. А Pivot переводится как "центр вращения". Запускаем интерпретатор:
 > c(my_func).
 > L=[23,6,2,9,27,400,78,45,61,82,14].
 > my_func:qsort(L).
 [2,6,9,14,23,27,45,61,78,82,400]
Как работает эта сортировка ? Сначала срабатывает вторая клауза функции qsort:
  [Pivot|T] = L.
которая связывает переменные Pivot -> 23 и T -> [6,2,9,27,400,78,45,61,82,14]. Теперь мы разделяем список Т на два списка : один из элементов, которые меньше чем Pivot, а второй - из элементов, которые больше или равны Pivot.
 4> Smaller = [X || X <- T, X < Pivot].
 [6,2,9,14]
 5> Bigger = [X || X <- T, X >= Pivot].
 [27,400,78,45,61,82]
Теперь мы можем отсортировать списки Smaller и Bigger и соединить их обратно с Pivot:
 qsort( [6,2,9,14] ) ++ [23] ++ qsort( [27,400,78,45,61,82] )
 = [2,6,9,14] ++ [23] ++ [27,45,61,78,82,400]
 = [2,6,9,14,23,27,45,61,78,82,400]

Записи (record)

Записи обьявляются с помощью следующего синтаксиса:
 -record(Name, {
 key1 = Default1,
 key2 = Default2,
 ...
 key3,
 ...
 }).
В командном интерпретаторе вместо record нужно использовать rr. В вышеуказанном примере, Name - это имя всей этой записи. key1, key2 и так далее - это имена полей этой записи. Все эти имена должны быть атомами Эрланга. При этом, key1 и key2 имеют значения по-умолчанию (Default1 и Default2, соответственно), которые присваиваются этим полям при создании новой записи Name, если для них не указано другого значения. Поле key3 является изначально неопределенным полем записи.

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

Функции и модули

В основу эрланга положена модульная организация кода. Модули в свою очередь состоят из функций. Модули Эрланга сохраняются в файлах с расширением .erl. Модули должны быть откомпилированы перед тем как их содержимое будет готово к выполнению. Скомпилированный модуль будет иметь расширение .beam. Имя модуля должно совпадать с именем файла. В качестве примера создадим пару структур данных, представляющих собой прямоугольник и круг. Затем извлечем из этих структур значения длин сторон для прямоугольника и радиуса для круга. Например, вот так:
 > Rectangle = {rectangle, 10, 5}.
 > Circle = {circle, 4}.
 > {rectangle, Width, Ht} = Rectangle.
 > {circle, R} = Circle.
Теперь напишем функцию, вычисляющую площади прямоугольника и круга. Сначала напишем текст модуля geometry, который сохраним в отдельном файле geometry.erl:
 -module(geometry).
 -export([area/1]).
 area({rectangle, Width, Ht}) -> Width * Ht;
 area({circle, R}) -> 3.14159 * R * R.
 
В модуле мы записали 2 варианта функции area - они называются клаузами. У каждой клаузы есть заголовок, шаблон аргументов, тело, состоящее из выражений. Шаблоны аргументов у клауз должны быть взаимоисключающие. Теперь запускаем в интерпретаторе компиляцию модуля:
 > c(geometry).
У вас должен появиться бинарник geometry.beam, Теперь вызываем функцию area:
 > geometry:area({rectangle, 10, 5}).
 50
 > geometry:area({circle, 1.4}).
 6.15752
В следующем примере мы напишем два модуля. При этом один модуль будет использовать функцию другого модуля. У нас имеется список покупок, состоящий из имен и их количества:
 1> Buy = [{oranges,4}, {newspaper,1}, {apples,10}, {pears,6}, {milk,3}].
Первый модуль - shop.erl - будет возвращать стоимость одного товара в зависимости от его вида:
 -module(shop).
 -export([cost/1]).
 cost(oranges) -> 5;
 cost(newspaper) -> 8;
 cost(apples) -> 2;
 cost(pears) -> 9;
 cost(milk) -> 7.
Теперь напишем второй модуль shop2.erl, в котором хотим посчитать общую стоимость покупок:
 -module(shop2).
 -export([total/1]).
 total([{What, N}|T]) -> shop:cost(What) * N + total(T);
 total([]) -> 0.
Теперь в командной строке компилируем модули и проверяем:
 > c(shop).
 > c(shop2).
 > shop2:total([{milk,3}]).
 21
 > shop1:total(Buy).
 123
Синтаксис эрланга имеет три вида пунктуации:
1 Запятые (,) разделяют аргументы в вызовах функции, конструкторах данных и шаблонах.
2 Точки (.) (с последующим пробелом) разделяют функции и выражения в оболочке Эрланга.
3 Точка с запятой (;) разделяет клаузы, которые мы используем в различных контекстах: в объявлении функций, а так же в блоках case, if, try..catch и в выражениях receive.

Арность(arity) функции - это количество аргументов, принимаемых этой функцией. В Эрланге, две функции,объявленные в одном модуле, с одним именем, но разным количеством аргументов, представляют собой две различные функции - классический пример перегрузки.

В эрланге анонимные функции - fun - это функции без имени. Создадим переменную Z, присвоив ее анонимной функции:

 > Z = fun(X) -> 2*X end.
Применим ее:
 > Z(2).
 4
Эрланг - это функциональный язык программирования. Кроме всего прочего это означает, что анонимные функции могут быть переданы как аргументы для функции, а также, что функции (или анонимные функции) могут возвращать анонимные функции в качестве результата. Функция, которая возвращает другие функции, или же может принимать другие функции в качестве своих аргументов, называется функцией высшего порядка.

Модуль lists, входящий в стандартные библиотеки Эрланга, экспортирует несколько функций, которые принимают другие функции в качестве аргументов. Наиболее полезная из них это функция lists:map(F, L). Она возвращает список, созданный применением функции F к каждому элементу из списка L. В питоне есть похожая одноименная функция map. Создадим список и применим к ней функцию Z, созданную нами выше:

 > L = [1,2,3,4].
 > lists:map(Z, L).
 [2,4,6,8]
Другая полезная функция - lists:filter(P, L), она возвращает новый список из таких элементов E списка L, для которых функция P(E) равна true. Давайте создадим анонимную функцию Even(X), которая возвращает true, если Х - это четное число:
 > Even = fun(X) -> (X rem 2) =:= 0 end.
Применим ее:
 18> lists:filter(Even, [1,2,3,4,5,6,8]).
 [2,4,6,8]
Функции могут использоваться не только в качестве аргументов других функций (таких как map и filter). Функции могут также возвращаться другими функциями - это обобщение (generalization). Приведем пример - допустим, что у нас есть список чего-либо, предположим фруктов:
 > Fruit = [apple,pear,orange].
Теперь объявим функцию MakeTest(L), которая преобразует любой список (L) в функцию, которая проверяет, находится ли ее аргумент в этом списке L:
 > MakeTest = fun(L) -> (fun(X) -> lists:member(X, L) end) end.
 > IsFruit = MakeTest(Fruit).
lists:member(X, L) возвращает true если X находится в списке L, в противном случае она возвращает false. Давайте протестируем нашу функцию:
 > IsFruit(pear).
 true
Также мы можем использовать ее как аргумент функции lists:filter:
 > lists:filter(IsFruit, [dog,orange,cat,apple,bear]).
 [orange,apple]

Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье