Jump to content
xisto Community
Sign in to follow this  
Giniu

Mastering Erlang Part 2 – Erlang Sequential writing separate modules

Recommended Posts

Mastering Erlang part 2 – Erlang Sequential

Or: Writing separate modules

 

And there it is... as I promised here is second part of Erlang tutorial – it wasn’t long since I’ve got some time and doesn't have anything to do... First of all review what you should already know/have:

 

Installed Erlang and know how to start and quit from Erlang Virtual Machine (shell).

Know how to create and what name should have Erlang source files.

Know how to compile source you’ve just created.

Have some free time that you want to spend learning Erlang. :)

 

Ok... so I believe that you know/have all this... if you lack first three points refer to first part of this tutorial, if you lack fourth point, come back other time and you will be able to start over. I’ll try to keep it as short as possible, because introduction was in separate tutorial...

 

Some lazy part – or Number, booleans, expressions and operators

 

I think that first thing I should discuss is number expressions – currently we’ll talk only about them in shell not in modules, so you can start it and I’ll begin... (remember every line in Erlang that is definite over must have dot at the end)

 

Number expressions in Erlang are it's strong point – the only limit to number expression is amount of user's memory so you don't have to worry how large result will be (theoretically) and how much of memory reserve for it. Erlang understands numbers written in classic decimal notation that are integer:

 

12345.

 

or float:

 

123.45.

 

they can be positive:

 

+5.

 

which is same as:

 

5.

 

or negative:

 

-5.

 

it can also understand float numbers written in positive exponents mode (number multiplicated by 10 given to power n):

 

123.45e3.

 

which is the same as:

 

123.45e+3.

 

there can also be floats written in negative exponents mode:

 

123.45e-3.

 

But decimal base isn't only one that Erlang can understand, Erlang understands any base from 2 to 36 by only giving the base and number like this binary base:

 

2#1001101.

 

or hexadecimal base:

 

16#1fa2.

 

Also useful way of writing numbers is interpreting of character ASCII codes by just typing:

 

$A.

 

or when you cannot type the letter, by:

 

$\n.

 

As you see there are many ways of number interpreting in Erlang... there are also others values you can assign, like booleans which interpretation in Erlang is very easy:

 

true.

 

means true and for instance:

 

false.

 

means false :) - you can do some compares on numbers and get booleans. You can check if expression1 and expression2 are equal:

 

expression1 == expression2

 

or not equal:

 

expression1 /= expression2

 

also less than or equal:

 

expression1 =< expression2

 

less than:

 

expression1 < expression2

 

greater than or equal:

 

expression1 >= expression2

 

greater:

 

expression1 > expression2

 

All those comparisons work for any types – also when comparing different types, so 1 == 1.0 returns true... there is way for do exact comparisons – exactly equal to look like:

 

expression1 =:= expression2

 

and exactly not equal to:

 

expression1 =/= expression2

 

there are few examples with returned values of “equal” and “not equal” so you can better understand them:

 

1 == 1 (true)

1 /= 1 (false)

1 =:= 1 (true)

1 =/= 1 (false)

1 == 1.0 (true)

1 /= 1.0 (false)

1 =:= 1.0 (false)

1 =/= 1.0 (true)

1 == 2 (false)

1 /= 2 (true)

1 =:= 2 (false)

1 =/= 2 (true)

 

So... you can now compare two numbers – but now – lets make some numbering arithmetics so you can do better comparisons. You can add two numbers:

 

a + b

 

subtract:

 

a – b

 

multiply:

 

a * b

 

floating point divide:

 

a / b

 

from this point arguments cannot be float – they must be integer. First of integer operands is integer divide:

 

a div b

 

get reminder of a / b:

 

a rem b

 

that are all classic arithmetics available in Erlang (rest are functions in math module), but there are also bitwise arithmetics, like unary bitwise not:

 

bnot a

 

bitwise and:

 

a band b

 

bitwise or:

 

a bor b

 

bitwise xor:

 

a bxor b

 

bitshift left:

 

a bsl b

 

bitshift right:

 

a bsr b

 

I think that all this requires some examples (I write for example a + b = c without classic Erlang writings to keep it short...)

 

2 + 2 = 4

2 + 3.1 = 5.10000

4 – 2 = 2

4 – 2.1 = 1.90000

2 * 2 = 4

2 * 2.1 = 4.20000

4 / 2 = 2.00000

4 / 2.1 = 1.90476

4 div 2 = 2

4 div 2.1 (error)

5 div 2 = 2

6 div 2 = 3

4 rem 2 = 0

5 rem 2 = 1

6 rem 2 = 0

bnot 0 = -1 (0=...00000000; -1=...11111111)

bnot 1 = -2 (1=...00000001; -2=...11111110)

7 band 8 = 0 (7=...0111; 8=...1000; 0=...0000)

7 band 12 = 4 (7=...0111; 12=...1100; 4=...0100)

1 bor 1 = 1 (1=...01)

1 bor 2 = 3 (1=...01; 2=...10; 3=...11)

1 bxor 2 = 3 (1=...001; 2=...010; 3=...011)

3 bxor 6 = 5 (3=...011; 5=...110; 2=...101)

2 bsl 1 = 4 (2=...0010; 4=...0100)

3 bsl 2 = 12 (3=...00011; 12=...01100)

12 bsr 1 = 6 (12=...01100; 6=...00110)

7 bsr 2 = 1 (7=...0111; 1=...0001)

 

So you should know how to make some arithmetics... try them all and remember to put dot at end, like:

 

1>2+2. 4

 

Now you can see that comparisons are a little better:

 

(2 + 2) == (7 band 12).

is true... but this is still weak, we need also some boolean expressions to see tell what if something or something will happened... first is simplest one – unary logical not:

 

not expression

 

we have also logical and:

 

expression1 and expression2

 

logical or:

 

expression1 or expression2

 

and last one – logical xor:

 

expression1 xor expression2

 

again lets look at some examples:

 

not true = false

not false = true

true or false = true

true or true = true

false or false = false

true and false = false

true and true = true

false and false = false

true xor false = true

true xor true = false

false xor false = false

 

Those basic boolean expressions gives large power and now your comparisons can looks like:

 

(2==2) and ((2+2)==(7 band 12)).

will return true. Sometimes evaluating expression takes very long and most languages still evaluates them – for example they first evaluate left then right side of and operator and then returns value – true or false... but this can take long... and what if on left we will get false? Other languages and this method in Erlang also counts right side... but there are special short-circuit boolean operators that allow to check only left side and right side only if needed. First of those two operators is orelse:

 

expression1 orelse expression2

 

will check expression1 and if it is true, will return true for whole expression and if expression1 is false it will return expression2 without checking it. Second short-circuit boolean operator is andalso:

 

expression1 andalso expression2

 

this checks expression1 and if it is false, whole expression returns false, if expression1 is true, Erlang returns expression2 as result of whole expression. The effect is same as using usual “or” and “and” but this way is a lot faster. Unfortunately this cannot be used in “guard” like in “case” or “if”, but I will talk about this later... It’s the end of this long chapter – now you can use numbers and booleans in Erlang... we’ll be coming back to other data types as we’ll need them – now let's do something more interesting...

 

Hearth of Erlang – or Functions and variables

 

Erlang as you know from previous part of this tutorial is functional language, it contains almost only functions and they are most important part of language. I’ll discuss functions using several examples, but first few words about variables.

 

In Erlang there is one thing that confuses at start – one variable can be bound only once in one function – so when you write N=2 this is correct but then N cannot change it's value, if you write N=N+1 you’ll get error, but writing M=N+1 will be all right with exception that now M=3 and you cannot bound M to any other variable. This makes special use of recursion in Erlang that is real fast because of that. But some more about variables – variables names can be any string staring with upper-case letter or underscore, containing only letters, numbers, underscores and “@” signs. Some good defined variables are:

 

X

N

Variable

Variable1

Other_variable

_myVariable1

_other_myVariable@somewhere

 

but not:

 

x

n

variable#

 

or anything else that violates rules. Underscored variables like “_myVariable1” wont be reported by compiler if it is unused – this is difference to usual upper-case letter variables, which are strictly reported, Erlang is very strict about errors and informs you about all disfunctions of your code. There are special types of variables, anonymous variable – it is used when you have to get value, but you don't care what that value is and don't want to store it anywhere. Anonymous variable is underscore “_”. Later on I’ll tell in what situations anonymous variable are used (when you match tuples or lists, but this is not the place for this now).

 

You can test variables also in shell without need to write module. We’ll try now:

 

N=1.

You assigned value “1” to variable “N”, now call it:

 

N.

And you get value. Also see that you cannot assign value to N:

 

N=2.

gives you error – this is one of confusing things in Erlang, but you can get used to it. In shell you can see what variables are binded by using function:

 

b().

from “bindings”. Then you can remove binding to some variable (warning – this works only in shell) by using function:

 

f(Variable).

From “forget” or unbind all variables:

 

f().

You can also see last 20 commands by using function:

 

h().

from “history” and execute n-th command in history by:

 

e(n).

from “evaluate”, or execute last command using function:

 

e(-1).

This works of course only in shell and can be useful when you are trying many examples with different variables. Now let's take a closer look at modules and functions, first I’ll give you some general look of function:

 

function(Patern1) ->

Action1,

Action1,

Action1;

function(Patern2) ->

Action2,

Action2,

Action2;

function(Patern3) ->

Action3,

Action3,

Action3;

...

function(PaternN) ->

ActionN,

ActionN,

ActionN.

 

Which can be extended with guards. But I won't explain it now - we analyse example that returns number e, then we add few functions and everything will show when it should. We call this module mymath. Create appropriate file that contains:

 

-module(mymath). -export([e/0]).  e() ->           2.71829.

now compile it and run:

 

mymath:e().

and you got right value... so now – lets explain it:

 

-module(mymath).

 

Names the module so it can be called – rather easy thing – just name.

 

-export([e/0]).

 

Function e containing 0 arguments can be called outside this module – any other function can be called only from this module.

 

e() ->

 

If function e is called without arguments, it returns...

 

2.71829.

 

this value. So... this one was easy, right? Create something more... let's add famous factorial to our module:

 

-module(mymath). -export([e/0, factorial/1]).  e() ->           2.71829.  factorial(1) ->           1; factorial(N) ->           N * factorial(N-1).

So let me explain (only lines that changed):

 

-export([e/0, factorial/1]).

 

Function e containing 0 arguments and function factorial containing 1 argument can be called outside this module – any other function can be called only from this module.

 

factorial(1) ->

 

If function factorial is called with one argument that is equal to 1...

 

1;

 

return one, but this is not end of function, because...

 

factorial(N) ->

 

If function factorial is called with one argument that isn't equal to 1...

 

N * factorial(N-1).

 

Return that value multiplicated by value of factorial from one less (this starts recursion, because function factorial is called again, and again, and again... until it will be factorial from one) and this is end of this function. Well this example works – but has a large bug – it can hang when we call it with value smaller than 1 – to count factorial(0) it needs factorial(-1), to count factorial(-2) it needs factorial(-3) and so on – it stops after you kill it or computer hangs from lack of resources... let's modify it with when command and a guard (condition)... also let's include 0!=1 and some easy information that wrong number was typed (returns -1 when other fails):

 

-module(mymath). -export([e/0, factorial/1]).  e() ->           2.71829.  factorial(N) when N>0 ->           N * factorial(N-1); factorial(0) ->           1; factorial(_) ->           -1.

we have some changes only in factorial function block – as you mentioned (or not) function are checked from top, and if some suites given conditions don't even look at others... ok – so let's explain this one:

 

factorial(N) when N>0 ->

 

this is easy – it do what it says - If function factorial is called with one argument that greater than 0 (the condition after word “when” is called guard. I’ll tell you something about them right after this example)...

 

N * factorial(N-1);

 

Return that value multiplicated by value of factorial from one less...

 

factorial(0) ->

 

If function factorial is called with one argument that is equal to 0...

 

1;

 

return one, but this is not end of function, because...

 

factorial(_) ->

 

when value wasn't greater than zero, and wasn't zero – it must me less than zero, so (we know that there is some argument, but we don't care about it, we use anonymous variable because we don't want to set memory for nothing) we...

 

-1.

 

return value -1 that can be interpreted by us as error. And this is end of our function. This isn't also good – how someone can know that -1 means to you error? I introduce now function format from module io, that allows you to write something to Erlang shell – right now I won’t tell you about all it's features, but that to write plain message, use something like this:

 

io:format("message~n", []).

 

you’ll understand why we use that [] when you’ll know about lists – now this is enough to fit our example:

 

-module(mymath). -export([e/0, factorial/1]).  e() ->           2.71829.  factorial(N) when N>0 ->           N * factorial(N-1); factorial(0) ->           1; factorial(_) ->          io:format("WARINING!!! Factorial can be counted only from numbers larger or equal zero!~n", []).

This don't require more comment – I think so... like I promised – I’ll tell something more about guards... As guard you can use any condition and special functions, like:

 

is_number(X) - X is a number

is_integer(X) - X is an integer

is_float(X) - X is a float

is_atom(X) - X is an atom

is_tuple(X) - X is a tuple

is_list(X) - X is a list

A == C - A is equal to C – you can use any condition

 

you can join guards into guard sequences, when you separate them with coma, guard sequence would be true if all guards are true and when you separate guards with semicolon, guard sequence is true when any of guards is true. So what if in our example someone calls function with number that isn't integer? We shouldn't count then... and when someone calls, it gets error – some things cannot happen – so let's modify our example:

 

-module(mymath). -export([e/0, factorial/1]).  e() ->           2.71829.  factorial(N) when N>0,is_integer(N) ->           N * factorial(N-1); factorial(0) ->           1; factorial(_) ->          io:format("WARINING!!! Factorial can be counted only from integers larger or equal zero!~n", []).

I feel that I don't have to explain this to you – compile it and try different values... this is first step into robustness in Erlang, but now at sequential stage. Now as exercise extend our example with function “add” that adds two given numbers, make sure they are numbers, if not – inform about error.

 

Structures of Erlang – or Atoms, tuples and lists

 

I believe that when you was adding this function you was interested – how can I add for example values in centimeters and in meters? How Erlang would know what add to what? We can do this several ways – fist – easy one, that introduces atoms and tuples, it will be divided into three examples... first we’ll talk about atoms... atoms are literal values – they are it's own type... but look out – they are VALUES not VARIABLES so you cannot assign to them, but you can assign them. Atoms should start with lower-case letter, can contain letters, numbers, underscore and “@” sign. If you want them contain some other characters, give them in single quotes. There are some examples:

 

this_is_atom

thisAlsoIsAtom

'And this also...'

 

Ok... let's see some example that print lengths:

 

-module(length). -export([printlength/2]).  printlength(N, cm) ->           printcm(N); printlength(M, m) ->           printm(M).  printcm(N) ->          io:format("there is ~w centimetres~n", [N]).  printm(M) ->          io:format("there is ~w meters~n", [M]).

I think that this example requires some explanation... outside we have function printlength with two arguments... now let's explain functions:

 

printlength(N, cm) ->

printcm(N);

 

if second argument is atom “cm” we call inner function primtcm with value from argument one as it's only argument...

 

printlength(M, m) ->

printm(M).

 

and same there - if second argument is atom “m” we call inner function primtm with value from argument one as it's only argument. The inner functions have looks like this, but to explain why, I first must you tell about lists and this will happened soon. Now try this example with few values, like:

 

length:printlength(25, cm).

or

 

length:printlength(4, m).

now... this is a bit confusing – if for example we would like to convert something we cannot use something like that, because we wouldn't be able to tell from and to what we are converting... so Erlang allows us to bind some values together and create a tuple. Tuples can bound any number of variables, like:

 

{5, cm}

{1, 2, 3}

 

but they can also be nested:

 

{5, {1, 4, {2, {cm}, 2}, 1, 1, {2, 2}}}

 

so, we can use things like first line of this example to specify that 5 is in centimeters. Let's modify our example:

 

-module(length). -export([printlength/2]).  printlength({N, cm}) ->           printcm(N); printlength({M, m}) ->           printm(M).  printcm(N) ->          io:format("there is ~w centimeters~n", [N]).  printm(M) ->          io:format("there is ~w meters~n", [M]).

this easy modification doesn't require more explanation... call modified function by:

 

length:printlength({25, cm}).

or

 

length:printlength({4, m}).

now if we are converting, we know that 25 is in centimeters and 4 is in meters because they are binded with each other. But tuples aren't also enough for most problems... we have to know what number of elements it have... and how can we add many values? There with help comes lists... lists can looks like:

 

[1, 2, 3, 4, 5]

 

they can also contain values of other types like for example tuples or atoms, or all of them at once:

 

[{1, 4}, {1, 3, 4}, mc, 21]

 

Now... let's see some example and I’ll explain it:

 

-module(addmany). -export([addmany/1]).  addmany([]) ->           0; addmany([First|Rest]) ->           First + addmany(Rest).

And now some explanation:

 

addmany([]) ->

0;

 

sign [] means empty list, so – if list is empty return 0, and if it isn't:

 

addmany([First|Rest]) ->

First + addmany(Rest).

 

Interpret list as [First|Rest] – something like [ ... | ... ] is extracting – it extracts first list element and bind it to variable First, and rest of list will be binded to variable Rest. More about tuple and lists matching goes just after some example that covers all of above three examples... but back to this example, it extract first element and returns sum of it and of sum of rest of list – this is also recursion... Now let's gather this example and create module that doesn’t add lengths in meters and centimeters and give us results in centimeters:

 

-module(addlength). -export([addlength/1]).  addlength([]) ->          0; addlength([First|Rest]) ->          addelement(First) + addlength(Rest).  addelement({Value, cm}) ->          Value; addelement({Value, m}) ->          Value * 100.

and now try this example:

 

addlength:addlength([{1, m}, {32, cm}, {17, cm}, {2.4, m}]).

You should get 389. So now you see that list combined with tuples and atoms are real powerful tool.

 

Now few word about matching of tuples... This exercise will be made in shell so start it and you will be able to do it during reading...

 

Tuple can be binded only to tuple with same number of elements or single variable, try:

 

A = {1, 2}.

and

 

{B, C} = {1, 2}.

or

 

{B, B, C, C, C} = {1, 1, 2, 2, 2}

now try:

 

A.

B.

and

 

C.

so you know how it works... but sometimes you don't care about value so you can use anonymous variable:

 

{_, D} = {wontseeme, yes}.

Now try:

 

D.

So... this is all about tuples you should know now, but now let's take a look at matching of lists... there is something more... We’ll play a bit with lists:

 

E=[1, 2, 3, 4, 5, 6].

E. gives [1, 2, 3, 4, 5, 6]

 

[L, M]=[1,2].

L. gives 1

M. gives 2

 

[F|Rest]=[1, 2, 3, 4, 5, 6].

F. gives 1

Rest. gives [2, 3, 4, 5, 6]

 

[G,H,TheRest]=[1, 2, 3, 4, 5, 6]

G. gives 1

H. gives 2

TheRest. gives [3, 4, 5, 6]

 

[I, J | K] = [1, 2].

I. gives 1

J. gives 2

K. gives []

 

Low let's see some examples that returns errors:

 

{A, A, B} = {1, 2, 3}

 

{A, B, C} = {1, 2}

 

[A, A, B] = [1, 2, 3]

 

[A, B, C] = [1, 2]

 

so as you see when you match list with list and tuple with tuple size and length should be same and you shouldn't assign different values to one variable.

 

So as you see – lists are one of great Erlang things, you can change order of list, search for some elements, count their length... they can be compared to arrays from other languages, but you don't have to reserve memory for them so they can grow as far as you want while using ready application.

 

I told you that I would explain a little about io:format – so there it goes – function format from standard module io gives you chance to write output to shell, I won’t tell you everything, because you now don't need to know more... it contains two arguments: first a string with break sequences (like ~w and ~n) and with with list of arguments... every argument from list is entered into place of ~w break sequence, ~n sequence means new line. There are some examples:

 

io:format("This is test when we have one variable:~w... did you saw that?~n", [Value1]). io:format("And now we have two variables:~w~w did you saw that? No space and enter?", [Value1, Value2]). io:format("Ahh... I forgot about that:~w ~w!~n", [Value1, Value2]).

This should be enough about that right now, but if this is not enough, run Erlang like this:

 

erl -man io

To get help about module io.

 

As exercise write module that contains tree functions: reverting list, finding max and min and returning it as tuple {min, max} also counting it's length – don't use pre-made functions or built in functions – this is exercise so do your best – only exception can be module io.

 

Conditions in Erlang – or If and case

 

First I’ll take care of if command – and again – in Erlang it looks different than in most languages... it doesn't have “else” but can have many conditions. Generally it looks like this:

 

if

GuardCondition1 ->

Action1;

GuardCondition2 ->

Action2;

GuardCondition3 ->

Action3;

...

GuardConditionN ->

ActionN

end

 

so – this is how it looks like – it checks if condition1 is satisfied and if yes, activates action1 and don't looks for other condition, but goes where end is – if you want to use “else”, just add as many conditions as you want and you’ll get even better result, you also can use “true” as condition to make sure something will happened, if no condition is true, error would happened and you don't want it to came up. There is small example of function using if, but I won't analyze it, you should made it by yourself:

 

compare(X, Y) ->

if

X>Y ->

greater;

X==Y ->

equal;

true ->

less

end.

 

Guard conditions in if can be joined using comma (Expr1,Expr2 - both must be true) or using semicolon (Expr1;Expr2 - just one them must be true), take a look at this example:

 

compare(X, Y) ->

if

X>Y,Y>0 ->

positive_x_is_largest;

X==Y ->

equal;

X<0 ->

x_is_negative

true ->

less_or_different_signs

end.

 

In Erlang there is also other construct that allows conditional execution, it is case – this is how it looks like in general:

 

case Expression of

Value1 ->

Action1;

Value2 ->

Action2;

Value3 ->

Action3;

...

ValueN ->

ActionN

end

 

In Erlang case is very similar to if. It can be the same as if command if it's formed like:

 

if

Expression==Value1 ->

Action1;

Expression==Value2 ->

Action2;

Expression==Value3 ->

Action3;

...

Expression==Value3 ->

ActionN

end

 

Case can have addictional guard sequence – then it looks like:

 

case Expression of

Value1 when GuardExpression1 ->

Action1;

Value2 when GuardExpression2 ->

Action2;

Value3 when GuardExpression3 ->

Action3;

...

ValueN when GuardExpression4 ->

ActionN

end

 

Action is executed when Expression is equal Value and GuardExpression is true. If any Value and GuardExpression cannot be satisfied, error occurs. You can get “else”-like instruction, by giving anonymous variable as last value and true as last GuardExpression (or by not giving last GuardExpression). Some example use of case can looks like:

 

case Length of

{X, cm} when X>0 ->

Length;

{Y, m} when Y>0 ->

Length * 100;

_ ->

0

end

 

Now you have powerful tool – test clauses of Erlang... this is time for you to show up some invention... there is the exercise:

 

Write module that takes two dates and gives how many days there was between them (form same dates there is 0 days). Hint: use tuples to keep dates and some cases to find out what year have a leap, make sure right dates are given, check if months don't have too much days, year too much months, first date isn't before second one and so on. You should be able to accomplish this without troubles...

 

Power of functional language – or Build-in functions and higher order functions

 

This is last part of informations needed to unleash Sequential Erlang. First I’ll give you some information about BIF's – which stands for Build-in functions. Complete list of Build-in functions can be found in man pages for Erlang. There are only some of them, they are all from module erlang, so you don't have to put module name before them:

 

date() – returns date in tuple (like {year, month, day})

time() – returns time in tuple (like {hour, minute, second})

trunc(Number) – returns truncated number (like 6 -> 6 but 5.6 -> 5)

round(Number) – returns rounded number (like 6 -> 6 but 5.6 -> 6)

length(List) – returns number of elements in list (like [1,2,3,4] -> 4)

size(Tuple) – returns size of tuple (like {1, 2} = 2)

float(Number) – converts number to float (like 5 -> 5.00000)

is_atom(Element) – returns true if Element is atom, else return false

is_tuple(Element) – returns true if Element is tuple, else return false

is_list(Element) – returns true if Element is list, else return false

atom_to_list(Atom) – return list that contains atom (like atom -> “atom”)

list_to_atom(List) – returns atom that contains list (like “list” -> list)

integer_to_list(Integer) – returns list that contains integer number (like 22 -> “22”)

 

there are much more Build-in functions that can be used – like I said earlier complete list of them is in manual pages for erlang (erl -man erlang). This is all I say about BIFs – now the real power – higher order functions – The higher order function looks like this:

 

fun

(Patern1) ->

Action1;

(Patern2) ->

Action2;

(Patern3) ->

Action3;

...

(PaternN) ->

ActionN

end

 

higher order function can also take extra Guard (when) parameter and looks like:

 

fun

(Patern1) when Expression1 ->

Action1;

(Patern2) when Expression2 ->

Action2;

(Patern3) when Expression3 ->

Action3;

...

(PaternN) when ExpressionN ->

ActionN

end

 

fun should be assigned to variable and then it can be called be calling it's assignment name. This can be seen in this shell example:

 

Function = fun          ({X, cm}) when X>0 ->                     io:format(“~w~n”,[X]);          ({Y, m}) when Y>0 ->                     io:format(“~w~n”,[Y]);          (_) ->                     0 end.

And now we can call it by typing:

 

Function({20, m}).

or any other value like in earlier example. This gives us real large power. In module lists there is function foreach that looks something like this:

 

foreach(Fun, [First|Rest]) ->

Fun(First),

foreach(Fun, Rest);

foreach(Fun, []) ->

ok.

 

We use it's predefined version (lists:foreach) to demonstrate it's use on our function “Function”, type in shell:

 

lists:foreach(Function, [{20, cm}, {5, m}, {3, m}, {1.2, m}]).

in lists module there are also other useful functions, but one I mention there – it is map – it looks like:

 

lists:map(Function, List)

 

and calls Function for every element in List. The result is also stored in List. If for example you have function that adds one to element and call this:

 

lists:map(add1, [1, 2, 3, 4, 5]).

you’ll get:

 

[2, 3, 4, 5, 6]

As you see you can change behavior of module not by calling different functions but by substituting function instances. Understand of fun object is one of most important thing. Read manual for erlang module and take a closer look at other BIFs, because you will be using them many times.

 

Now as exercise rewrite all modules from exercises using build-in functions and higher order functions, make sure module won't hang when you give wrong values and won't crash with Erlang error, but gives information what is wrong, especially when you will be modifying exercise from part about if and case – do some research about module io (use manual pages), find out how user can input character while application is running, make him enter his date of birth, and calculate how many days he lived. Make input function a separate one, export three of them – one that asks for date of birth, second in which date of birth is given through arguments and third in which two dates are given thought arguments. This one should take you some time – if you want to make them good – remember – Erlang cannot crash – on real-time systems crashes are critical – If you won’t take care – a plane falls!!!

 

This is the end of this part. You know all basic ways of sequential programming in Erlang. Next time we will take care for concurrency, so I will discuss more module options and module communication. Be sure to understand this part because you will have hard times going through next one. If anything from this or earlier part requires some more explanation – just post information there or PM and I will try to do my best to help you. Next part will take some time to write – sorry... but I think that you will have something to do :) And at least – when Erlang Everyday comes to true, I will fill holes in sequential and concurrent Erlang so you will be able to create even complicated applications. This one is long way too long, so see you next time :)

 

--------------------------

Thanks for correction of this tutorial goes to Nelle... Big thanks...

 

PS.: The copy of this tutorial is at AntiLost... there are still edit buttons so all changes/fixes would be made there... if you want to take a look and see if something changed, go to http://forums.xisto.com/no_longer_exists/ and visit board. SeeYa next time...

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×
×
  • Create New...

Important Information

Terms of Use | Privacy Policy | Guidelines | We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.