Most operating systems have a command interpreter or shell- Unix
and Linux have many, while Windows has the Command Prompt. Erlang has
its own shell where you can directly write bits of Erlang code
and evaluate (run) them to see what happens (see
% erl Erlang R15B (erts-5.9.1) [source] [smp:8:8] [rq:8] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.9.1 (abort with ^G) 1>
Now type in "2 + 5." as shown below.
1> 2 + 5. 7 2>
In Windows, the shell is started by double-clicking on the Erlang shell icon.
You'll notice that the Erlang shell has numbered the lines that
can be entered, (as 1> 2>) and that it has correctly told you
that 2 + 5 is 7! Also notice that you have to tell it you are
done entering code by finishing with a full stop "." and a
carriage return. If you make mistakes writing things in the shell,
you can delete things by using the backspace key as in most
shells. There are many more editing commands in the shell
(See the chapter
(Note: you will find a lot of line numbers given by the shell out of sequence in this tutorial as it was written and the code tested in several sessions.)
Now let's try a more complex calculation.
2> (42 + 77) * 66 / 3. 2618.0
Here you can see the use of brackets and the multiplication
operator "*" and division operator "/", just as in normal
arithmetic (see the chapter
To shutdown the Erlang system and the Erlang shell type Control-C. You will see the following output:
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded (v)ersion (k)ill (D)b-tables (d)istribution a %
Type "a" to leave the Erlang system.
Another way to shutdown the Erlang system is by entering
3> halt(). %
A programming language isn't much use if you can just run code
from the shell. So here is a small Erlang program. Enter it into
a file called
-module(tut).
-export([double/1]).
double(X) ->
2 * X.
It's not hard to guess that this "program" doubles the value of numbers. I'll get back to the first two lines later. Let's compile the program. This can be done in your Erlang shell as shown below:
3> c(tut). {ok,tut}
The
Now let's run the program.
4> tut:double(10). 20
As expected double of 10 is 20.
Now let's get back to the first two lines. Erlang programs are
written in files. Each file contains what we call an Erlang
module. The first line of code in the module tells us
the name of the module (see the chapter
-module(tut).
This tells us that the module is called tut. Note
the "." at the end of the line. The files which are used to store
the module must have the same name as the module but with
the extension ".erl". In our case the file name is
4> tut:double(10).
means call function
The second line:
-export([double/1]).
says that the module
Now for a more complicated example, the factorial of a number
(e.g. factorial of 4 is 4 * 3 * 2 * 1). Enter the following code
in a file called
-module(tut1).
-export([fac/1]).
fac(1) ->
1;
fac(N) ->
N * fac(N - 1).
Compile the file
5> c(tut1). {ok,tut1}
And now calculate the factorial of 4.
6> tut1:fac(4). 24
The first part:
fac(1) ->
1;
says that the factorial of 1 is 1. Note that we end this part with a ";" which indicates that there is more of this function to come. The second part:
fac(N) ->
N * fac(N - 1).
says that the factorial of N is N multiplied by the factorial of N - 1. Note that this part ends with a "." saying that there are no more parts of this function.
A function can have many arguments. Let's expand the module
-module(tut1).
-export([fac/1, mult/2]).
fac(1) ->
1;
fac(N) ->
N * fac(N - 1).
mult(X, Y) ->
X * Y.
Note that we have also had to expand the
Compile:
7> c(tut1). {ok,tut1}
and try it out:
8> tut1:mult(3,4). 12
In the example above the numbers are integers and the arguments
in the functions in the code,
Atoms are another data type in Erlang. Atoms start with a small
letter ((see the chapter
Enter the next program (file:
-module(tut2).
-export([convert/2]).
convert(M, inch) ->
M / 2.54;
convert(N, centimeter) ->
N * 2.54.
Compile and test:
9> c(tut2). {ok,tut2} 10> tut2:convert(3, inch). 1.1811023622047243 11> tut2:convert(7, centimeter). 17.78
Notice that I have introduced decimals (floating point numbers) without any explanation, but I guess you can cope with that.
See what happens if I enter something other than centimeter or inch in the convert function:
12> tut2:convert(3, miles). ** exception error: no function clause matching tut2:convert(3,miles) (tut2.erl, line 4)
The two parts of the
13> v(12). {'EXIT',{function_clause,[{tut2,convert, [3,miles], [{file,"tut2.erl"},{line,4}]}, {erl_eval,do_apply,5,[{file,"erl_eval.erl"},{line,482}]}, {shell,exprs,7,[{file,"shell.erl"},{line,666}]}, {shell,eval_exprs,7,[{file,"shell.erl"},{line,621}]}, {shell,eval_loop,3,[{file,"shell.erl"},{line,606}]}]}}
Now the
tut2:convert(3, inch).
Does this mean that 3 is in inches? or that 3 is in centimeters and we want to convert it to inches? So Erlang has a way to group things together to make things more understandable. We call these tuples. Tuples are surrounded by "{" and "}".
So we can write
-module(tut3).
-export([convert_length/1]).
convert_length({centimeter, X}) ->
{inch, X / 2.54};
convert_length({inch, Y}) ->
{centimeter, Y * 2.54}.
Compile and test:
14> c(tut3). {ok,tut3} 15> tut3:convert_length({inch, 5}). {centimeter,12.7} 16> tut3:convert_length(tut3:convert_length({inch, 5})). {inch,5.0}
Note on line 16 we convert 5 inches to centimeters and back
again and reassuringly get back to the original value. I.e
the argument to a function can be the result of another function.
Pause for a moment and consider how line 16 (above) works.
The argument we have given the function
We have shown tuples with two parts above, but tuples can have as many parts as we want and contain any valid Erlang term. For example, to represent the temperature of various cities of the world we could write
{moscow, {c, -10}}
{cape_town, {f, 70}}
{paris, {f, 28}}
Tuples have a fixed number of things in them. We call each thing
in a tuple an element. So in the tuple
Whereas tuples group things together, we also want to be able to represent lists of things. Lists in Erlang are surrounded by "[" and "]". For example a list of the temperatures of various cities in the world could be:
[{moscow, {c, -10}}, {cape_town, {f, 70}}, {stockholm, {c, -4}},
{paris, {f, 28}}, {london, {f, 36}}]
Note that this list was so long that it didn't fit on one line. This doesn't matter, Erlang allows line breaks at all "sensible places" but not, for example, in the middle of atoms, integers etc.
A very useful way of looking at parts of lists, is by using "|". This is best explained by an example using the shell.
17> [First |TheRest] = [1,2,3,4,5]. [1,2,3,4,5] 18> First. 1 19> TheRest. [2,3,4,5]
We use | to separate the first elements of the list from
the rest of the list. (
Another example:
20> [E1, E2 | R] = [1,2,3,4,5,6,7]. [1,2,3,4,5,6,7] 21> E1. 1 22> E2. 2 23> R. [3,4,5,6,7]
Here we see the use of | to get the first two elements from the list. Of course if we try to get more elements from the list than there are elements in the list we will get an error. Note also the special case of the list with no elements [].
24> [A, B | C] = [1, 2]. [1,2] 25> A. 1 26> B. 2 27> C. []
In all the examples above, I have been using new variable names,
not reusing the old ones:
The following example shows how we find the length of a list:
-module(tut4).
-export([list_length/1]).
list_length([]) ->
0;
list_length([First | Rest]) ->
1 + list_length(Rest).
Compile (file
28> c(tut4). {ok,tut4} 29> tut4:list_length([1,2,3,4,5,6,7]). 7
Explanation:
list_length([]) ->
0;
The length of an empty list is obviously 0.
list_length([First | Rest]) ->
1 + list_length(Rest).
The length of a list with the first element
(Advanced readers only: This is not tail recursive, there is a better way to write this function.)
In general we can say we use tuples where we would use "records" or "structs" in other languages and we use lists when we want to represent things which have varying sizes, (i.e. where we would use linked lists in other languages).
Erlang does not have a string data type, instead strings can be
represented by lists of ASCII characters. So the list
30> [97,98,99]. "abc"
Maps are a set of key to value associations. These associations are encapsulated with "#{" and "}". To create an association from "key" to value 42, we write:
> #{ "key" => 42 }.
#{"key" => 42}
We will jump straight into the deep end with an example using some interesting features.
The following example shows how we calculate alpha blending using maps to reference color and alpha channels:
-module(color).
-export([new/4, blend/2]).
-define(is_channel(V), (is_float(V) andalso V >= 0.0 andalso V =< 1.0)).
new(R,G,B,A) when ?is_channel(R), ?is_channel(G),
?is_channel(B), ?is_channel(A) ->
#{red => R, green => G, blue => B, alpha => A}.
blend(Src,Dst) ->
blend(Src,Dst,alpha(Src,Dst)).
blend(Src,Dst,Alpha) when Alpha > 0.0 ->
Dst#{
red := red(Src,Dst) / Alpha,
green := green(Src,Dst) / Alpha,
blue := blue(Src,Dst) / Alpha,
alpha := Alpha
};
blend(_,Dst,_) ->
Dst#{
red := 0.0,
green := 0.0,
blue := 0.0,
alpha := 0.0
}.
alpha(#{alpha := SA}, #{alpha := DA}) ->
SA + DA*(1.0 - SA).
red(#{red := SV, alpha := SA}, #{red := DV, alpha := DA}) ->
SV*SA + DV*DA*(1.0 - SA).
green(#{green := SV, alpha := SA}, #{green := DV, alpha := DA}) ->
SV*SA + DV*DA*(1.0 - SA).
blue(#{blue := SV, alpha := SA}, #{blue := DV, alpha := DA}) ->
SV*SA + DV*DA*(1.0 - SA).
Compile (file
> c(color). {ok,color} > C1 = color:new(0.3,0.4,0.5,1.0). #{alpha => 1.0,blue => 0.5,green => 0.4,red => 0.3} > C2 = color:new(1.0,0.8,0.1,0.3). #{alpha => 0.3,blue => 0.1,green => 0.8,red => 1.0} > color:blend(C1,C2). #{alpha => 1.0,blue => 0.5,green => 0.4,red => 0.3} > color:blend(C2,C1). #{alpha => 1.0,blue => 0.38,green => 0.52,red => 0.51}
This example warrants some explanation:
-define(is_channel(V), (is_float(V) andalso V >= 0.0 andalso V =< 1.0)).
First we define a macro
new(R,G,B,A) when ?is_channel(R), ?is_channel(G),
?is_channel(B), ?is_channel(A) ->
#{red => R, green => G, blue => B, alpha => A}.
The function
By calling
The first thing
alpha(#{alpha := SA}, #{alpha := DA}) ->
SA + DA*(1.0 - SA).
We fetch the value associated with key
This is also the case for functions
red(#{red := SV, alpha := SA}, #{red := DV, alpha := DA}) ->
SV*SA + DV*DA*(1.0 - SA).
The difference here is that we check for two keys in each map argument. The other keys are ignored.
Finally we return the resulting color in
blend(Src,Dst,Alpha) when Alpha > 0.0 ->
Dst#{
red := red(Src,Dst) / Alpha,
green := green(Src,Dst) / Alpha,
blue := blue(Src,Dst) / Alpha,
alpha := Alpha
};
We update the
Erlang has a lot of standard modules to help you do things. For
example, the module
% erl -man io ERLANG MODULE DEFINITION io(3) MODULE io - Standard I/O Server Interface Functions DESCRIPTION This module provides an interface to standard Erlang IO servers. The output functions all return ok if they are suc- ...
If this doesn't work on your system, the documentation is included as HTML in the Erlang/OTP release, or you can read the documentation as HTML or download it as PDF from either of the sites www.erlang.se (commercial Erlang) or www.erlang.org (open source), for example for release R9B:
http://www.erlang.org/doc/r9b/doc/index.html
It's nice to be able to do formatted output in these example, so
the next example shows a simple way to use to use
the
31> io:format("hello world~n", []). hello world ok 32> io:format("this outputs one Erlang term: ~w~n", [hello]). this outputs one Erlang term: hello ok 33> io:format("this outputs two Erlang terms: ~w~w~n", [hello, world]). this outputs two Erlang terms: helloworld ok 34> io:format("this outputs two Erlang terms: ~w ~w~n", [hello, world]). this outputs two Erlang terms: hello world ok
The function
Now for a larger example to consolidate what we have learnt so far. Assume we have a list of temperature readings from a number of cities in the world. Some of them are in Celsius (Centigrade) and some in Fahrenheit (as in the previous list). First let's convert them all to Celsius, then let's print out the data neatly.
%% This module is in file tut5.erl
-module(tut5).
-export([format_temps/1]).
%% Only this function is exported
format_temps([])-> % No output for an empty list
ok;
format_temps([City | Rest]) ->
print_temp(convert_to_celsius(City)),
format_temps(Rest).
convert_to_celsius({Name, {c, Temp}}) -> % No conversion needed
{Name, {c, Temp}};
convert_to_celsius({Name, {f, Temp}}) -> % Do the conversion
{Name, {c, (Temp - 32) * 5 / 9}}.
print_temp({Name, {c, Temp}}) ->
io:format("~-15w ~w c~n", [Name, Temp]).
35> c(tut5). {ok,tut5} 36> tut5:format_temps([{moscow, {c, -10}}, {cape_town, {f, 70}}, {stockholm, {c, -4}}, {paris, {f, 28}}, {london, {f, 36}}]). moscow -10 c cape_town 21.11111111111111 c stockholm -4 c paris -2.2222222222222223 c london 2.2222222222222223 c ok
Before we look at how this program works, notice that we have
added a few comments to the code. A comment starts with a %
character and goes on to the end of the line. Note as well that
the
Note as well that when testing the program from the shell, I had to spread the input over two lines as the line was too long.
When we call
Here we see a function call as
Now we call
It could be useful to find the maximum and minimum temperature in lists like this. Before extending the program to do this, let's look at functions for finding the maximum value of the elements in a list:
-module(tut6).
-export([list_max/1]).
list_max([Head|Rest]) ->
list_max(Rest, Head).
list_max([], Res) ->
Res;
list_max([Head|Rest], Result_so_far) when Head > Result_so_far ->
list_max(Rest, Head);
list_max([Head|Rest], Result_so_far) ->
list_max(Rest, Result_so_far).
37> c(tut6). {ok,tut6} 38> tut6:list_max([1,2,3,4,5,7,4,3,2,1]). 7
First note that we have two functions here with the same name
This is an example where we walk through a list "carrying" a
value with us, in this case
In
Some useful operators in guards are, < less than, >
greater than, == equal, >= greater or equal, =< less or
equal, /= not equal. (see the chapter
To change the above program to one which works out the minimum
value of the element in a list, all we would need to do is to
write < instead of >. (But it would be wise to change
the name of the function to
Remember that I mentioned earlier that a variable could only be
given a value once in its scope? In the above we see, for example,
that
Another way of creating and giving a variable a value is by using
the match operator = . So if I write
39> M = 5. 5 40> M = 6. ** exception error: no match of right hand side value 6 41> M = M + 1. ** exception error: no match of right hand side value 6 42> N = M + 1. 6
The use of the match operator is particularly useful for pulling apart Erlang terms and creating new ones.
43> {X, Y} = {paris, {f, 28}}. {paris,{f,28}} 44> X. paris 45> Y. {f,28}
Here we see that
Of course if we try to do the same again with another city, we get an error:
46> {X, Y} = {london, {f, 36}}. ** exception error: no match of right hand side value {london,{f,36}}
Variables can also be used to improve the readability of
programs, for example, in the
list_max([Head|Rest], Result_so_far) when Head > Result_so_far ->
New_result_far = Head,
list_max(Rest, New_result_far);
which is possibly a little clearer.
Remember that the | operator can be used to get the head of a list:
47> [M1|T1] = [paris, london, rome]. [paris,london,rome] 48> M1. paris 49> T1. [london,rome]
The | operator can also be used to add a head to a list:
50> L1 = [madrid | T1]. [madrid,london,rome] 51> L1. [madrid,london,rome]
Now an example of this when working with lists - reversing the order of a list:
-module(tut8).
-export([reverse/1]).
reverse(List) ->
reverse(List, []).
reverse([Head | Rest], Reversed_List) ->
reverse(Rest, [Head | Reversed_List]);
reverse([], Reversed_List) ->
Reversed_List.
52> c(tut8). {ok,tut8} 53> tut8:reverse([1,2,3]). [3,2,1]
Consider how
reverse([1|2,3], []) =>
reverse([2,3], [1|[]])
reverse([2|3], [1]) =>
reverse([3], [2|[1])
reverse([3|[]], [2,1]) =>
reverse([], [3|[2,1]])
reverse([], [3,2,1]) =>
[3,2,1]
The module
Now let's get back to the cities and temperatures, but take a more structured approach this time. First let's convert the whole list to Celsius as follows and test the function:
-module(tut7).
-export([format_temps/1]).
format_temps(List_of_cities) ->
convert_list_to_c(List_of_cities).
convert_list_to_c([{Name, {f, F}} | Rest]) ->
Converted_City = {Name, {c, (F -32)* 5 / 9}},
[Converted_City | convert_list_to_c(Rest)];
convert_list_to_c([City | Rest]) ->
[City | convert_list_to_c(Rest)];
convert_list_to_c([]) ->
[].
54> c(tut7). {ok, tut7}. 55> tut7:format_temps([{moscow, {c, -10}}, {cape_town, {f, 70}}, {stockholm, {c, -4}}, {paris, {f, 28}}, {london, {f, 36}}]). [{moscow,{c,-10}}, {cape_town,{c,21.11111111111111}}, {stockholm,{c,-4}}, {paris,{c,-2.2222222222222223}}, {london,{c,2.2222222222222223}}]
Looking at this bit by bit:
format_temps(List_of_cities) ->
convert_list_to_c(List_of_cities).
Here we see that
[Converted_City | convert_list_to_c(Rest)];
or
[City | convert_list_to_c(Rest)];
We go on doing this until we get to the end of the list (i.e. the list is empty:
convert_list_to_c([]) ->
[].
Now we have converted the list, we add a function to print it:
-module(tut7).
-export([format_temps/1]).
format_temps(List_of_cities) ->
Converted_List = convert_list_to_c(List_of_cities),
print_temp(Converted_List).
convert_list_to_c([{Name, {f, F}} | Rest]) ->
Converted_City = {Name, {c, (F -32)* 5 / 9}},
[Converted_City | convert_list_to_c(Rest)];
convert_list_to_c([City | Rest]) ->
[City | convert_list_to_c(Rest)];
convert_list_to_c([]) ->
[].
print_temp([{Name, {c, Temp}} | Rest]) ->
io:format("~-15w ~w c~n", [Name, Temp]),
print_temp(Rest);
print_temp([]) ->
ok.
56> c(tut7). {ok,tut7} 57> tut7:format_temps([{moscow, {c, -10}}, {cape_town, {f, 70}}, {stockholm, {c, -4}}, {paris, {f, 28}}, {london, {f, 36}}]). moscow -10 c cape_town 21.11111111111111 c stockholm -4 c paris -2.2222222222222223 c london 2.2222222222222223 c ok
We now have to add a function to find the cities with the maximum and minimum temperatures. The program below isn't the most efficient way of doing this as we walk through the list of cities four times. But it is better to first strive for clarity and correctness and to make programs efficient only if really needed.
Converted_List = convert_list_to_c(List_of_cities),
print_temp(Converted_List),
{Max_city, Min_city} = find_max_and_min(Converted_List),
print_max_and_min(Max_city, Min_city).
convert_list_to_c([{Name, {f, Temp}} | Rest]) ->
Converted_City = {Name, {c, (Temp -32)* 5 / 9}},
[Converted_City | convert_list_to_c(Rest)];
convert_list_to_c([City | Rest]) ->
[City | convert_list_to_c(Rest)];
convert_list_to_c([]) ->
[].
print_temp([{Name, {c, Temp}} | Rest]) ->
io:format("~-15w ~w c~n", [Name, Temp]),
print_temp(Rest);
print_temp([]) ->
ok.
find_max_and_min([City | Rest]) ->
find_max_and_min(Rest, City, City).
find_max_and_min([{Name, {c, Temp}} | Rest],
{Max_Name, {c, Max_Temp}},
{Min_Name, {c, Min_Temp}}) ->
if
Temp > Max_Temp ->
Max_City = {Name, {c, Temp}}; % Change
true ->
Max_City = {Max_Name, {c, Max_Temp}} % Unchanged
end,
if
Temp < Min_Temp ->
Min_City = {Name, {c, Temp}}; % Change
true ->
Min_City = {Min_Name, {c, Min_Temp}} % Unchanged
end,
find_max_and_min(Rest, Max_City, Min_City);
find_max_and_min([], Max_City, Min_City) ->
{Max_City, Min_City}.
print_max_and_min({Max_name, {c, Max_temp}}, {Min_name, {c, Min_temp}}) ->
io:format("Max temperature was ~w c in ~w~n", [Max_temp, Max_name]),
io:format("Min temperature was ~w c in ~w~n", [Min_temp, Min_name]).]]>
58> c(tut7). {ok, tut7} 59> tut7:format_temps([{moscow, {c, -10}}, {cape_town, {f, 70}}, {stockholm, {c, -4}}, {paris, {f, 28}}, {london, {f, 36}}]). moscow -10 c cape_town 21.11111111111111 c stockholm -4 c paris -2.2222222222222223 c london 2.2222222222222223 c Max temperature was 21.11111111111111 c in cape_town Min temperature was -10 c in moscow ok
The function
if
Condition 1 ->
Action 1;
Condition 2 ->
Action 2;
Condition 3 ->
Action 3;
Condition 4 ->
Action 4
end
Note there is no ";" before
The following is a short program to show the workings of
-module(tut9).
-export([test_if/2]).
test_if(A, B) ->
if
A == 5 ->
io:format("A == 5~n", []),
a_equals_5;
B == 6 ->
io:format("B == 6~n", []),
b_equals_6;
A == 2, B == 3 -> %i.e. A equals 2 and B equals 3
io:format("A == 2, B == 3~n", []),
a_equals_2_b_equals_3;
A == 1 ; B == 7 -> %i.e. A equals 1 or B equals 7
io:format("A == 1 ; B == 7~n", []),
a_equals_1_or_b_equals_7
end.
Testing this program gives:
60> c(tut9). {ok,tut9} 61> tut9:test_if(5,33). A == 5 a_equals_5 62> tut9:test_if(33,6). B == 6 b_equals_6 63> tut9:test_if(2, 3). A == 2, B == 3 a_equals_2_b_equals_3 64> tut9:test_if(1, 33). A == 1 ; B == 7 a_equals_1_or_b_equals_7 65> tut9:test_if(33, 7). A == 1 ; B == 7 a_equals_1_or_b_equals_7 66> tut9:test_if(33, 33). ** exception error: no true branch found when evaluating an if expression in function tut9:test_if/2 (tut9.erl, line 5)
Notice that
convert_length({centimeter, X}) ->
{inch, X / 2.54};
convert_length({inch, Y}) ->
{centimeter, Y * 2.54}.
We could also write the same program as:
-module(tut10).
-export([convert_length/1]).
convert_length(Length) ->
case Length of
{centimeter, X} ->
{inch, X / 2.54};
{inch, Y} ->
{centimeter, Y * 2.54}
end.
67> c(tut10). {ok,tut10} 68> tut10:convert_length({inch, 6}). {centimeter,15.24} 69> tut10:convert_length({centimeter, 2.5}). {inch,0.984251968503937}
Notice that both
-module(tut11).
-export([month_length/2]).
month_length(Year, Month) ->
%% All years divisible by 400 are leap
%% Years divisible by 100 are not leap (except the 400 rule above)
%% Years divisible by 4 are leap (except the 100 rule above)
Leap = if
trunc(Year / 400) * 400 == Year ->
leap;
trunc(Year / 100) * 100 == Year ->
not_leap;
trunc(Year / 4) * 4 == Year ->
leap;
true ->
not_leap
end,
case Month of
sep -> 30;
apr -> 30;
jun -> 30;
nov -> 30;
feb when Leap == leap -> 29;
feb -> 28;
jan -> 31;
mar -> 31;
may -> 31;
jul -> 31;
aug -> 31;
oct -> 31;
dec -> 31
end.
70> c(tut11). {ok,tut11} 71> tut11:month_length(2004, feb). 29 72> tut11:month_length(2003, feb). 28 73> tut11:month_length(1947, aug). 31
Built in functions (BIFs) are functions which for some reason are
built in to the Erlang virtual machine. BIFs often implement
functionality that is impossible to implement in Erlang or is too
inefficient to implement in Erlang. Some BIFs can be called
by use of the function name only, but they by default belong
to the erlang module. So for example, the call to the BIF
As you can see, we first find out if a year is leap or not. If a
year is divisible by 400, it is a leap year. To find this out we
first divide the year by 400 and use the built in function
2004 / 400 = 5.01
trunc(5.01) = 5
5 * 400 = 2000
and we can see that we got back 2000 which is not the same as 2004, so 2004 isn't divisible by 400. Year 2000:
2000 / 400 = 5.0
trunc(5.0) = 5
5 * 400 = 2000
so we have a leap year. The next two tests, which check if the year is
divisible by 100 or 4, are done in the same way. The first
This example showed the use of
74> 2004 rem 400. 4
so instead of writing
trunc(Year / 400) * 400 == Year ->
leap;
we could write
Year rem 400 == 0 ->
leap;
There are many other built in functions (BIF) such as
75> trunc(5.6). 5 76> round(5.6). 6 77> length([a,b,c,d]). 4 78> float(5). 5.0 79> is_atom(hello). true 80> is_atom("hello"). false 81> is_tuple({paris, {c, 30}}). true 82> is_tuple([paris, {c, 30}]). false
All the above can be used in guards. Now for some which can't be used in guards:
83> atom_to_list(hello). "hello" 84> list_to_atom("goodbye"). goodbye 85> integer_to_list(22). "22"
The 3 BIFs above do conversions which would be difficult (or impossible) to do in Erlang.
Erlang, like most modern functional programming languages, has higher order functions. We start with an example using the shell:
86> Xf = fun(X) -> X * 2 end. #Fun<erl_eval.5.123085357> 87> Xf(5). 10
What we have done here is to define a function which doubles
the value of number and assign this function to a variable. Thus
foreach(Fun, [First|Rest]) ->
Fun(First),
foreach(Fun, Rest);
foreach(Fun, []) ->
ok.
map(Fun, [First|Rest]) ->
[Fun(First)|map(Fun,Rest)];
map(Fun, []) ->
[].
These two functions are provided in the standard module
88> Add_3 = fun(X) -> X + 3 end. #Fun<erl_eval.5.123085357> 89> lists:map(Add_3, [1,2,3]). [4,5,6]
Now let's print out the temperatures in a list of cities (yet again):
90> Print_City = fun({City, {X, Temp}}) -> io:format("~-15w ~w ~w~n", [City, X, Temp]) end. #Fun<erl_eval.5.123085357> 91> lists:foreach(Print_City, [{moscow, {c, -10}}, {cape_town, {f, 70}}, {stockholm, {c, -4}}, {paris, {f, 28}}, {london, {f, 36}}]). moscow c -10 cape_town f 70 stockholm c -4 paris f 28 london f 36 ok
We will now define a fun which can be used to go through a list of cities and temperatures and transform them all to Celsius.
-module(tut13).
-export([convert_list_to_c/1]).
convert_to_c({Name, {f, Temp}}) ->
{Name, {c, trunc((Temp - 32) * 5 / 9)}};
convert_to_c({Name, {c, Temp}}) ->
{Name, {c, Temp}}.
convert_list_to_c(List) ->
lists:map(fun convert_to_c/1, List).
92> tut13:convert_list_to_c([{moscow, {c, -10}}, {cape_town, {f, 70}}, {stockholm, {c, -4}}, {paris, {f, 28}}, {london, {f, 36}}]). [{moscow,{c,-10}}, {cape_town,{c,21}}, {stockholm,{c,-4}}, {paris,{c,-2}}, {london,{c,2}}]
The
lists:map(fun convert_to_c/1, List)
When we use a function defined elsewhere as a fun we can refer
to it as
The standard module
{Name, {c, trunc((Temp - 32) * 5 / 9)}};
convert_to_c({Name, {c, Temp}}) ->
{Name, {c, Temp}}.
convert_list_to_c(List) ->
New_list = lists:map(fun convert_to_c/1, List),
lists:sort(fun({_, {c, Temp1}}, {_, {c, Temp2}}) ->
Temp1 < Temp2 end, New_list).]]>
93> c(tut13). {ok,tut13} 94> tut13:convert_list_to_c([{moscow, {c, -10}}, {cape_town, {f, 70}}, {stockholm, {c, -4}}, {paris, {f, 28}}, {london, {f, 36}}]). [{moscow,{c,-10}}, {stockholm,{c,-4}}, {paris,{c,-2}}, {london,{c,2}}, {cape_town,{c,21}}]
In
Temp1 < Temp2 end,]]>
Here we introduce the concept of an anonymous variable
"_". This is simply shorthand for a variable which is going to
get a value, but we will ignore the value. This can be used
anywhere suitable, not just in fun's.