erlang入门(1)

1、fibo数组

-module(tut1).
-export([fac/1]).
fac(1) ->
1;
fac(N) ->
N * fac(N - 1).
5> c(tut1).
{ok,tut1}
6> tut1:fac(4).
24
注意,执行pwd().可以显示当前的erlang读取路径,通过cd(“c:\xxx”).来改变路径,一定在后面加上”.”

2、元子是在Erlang中的另一个数据类型。元子以小写字母开头,元子只是一个简单的名字,其它什么都不是。他们不像变量可以带有一个值。
-module(tut2).
-export([convert/2]).
convert(M, inch) ->
M / 2.54;
convert(N, centimeter) ->
N * 2.54.
9> c(tut2).
{ok,tut2}
10> tut2:convert(3, inch).
1.18110
11> tut2:convert(7, centimeter).
17.7800
看看如果我们输入centimeter和inch以外的参数给convert函数后会发生什么。
13> tut2:convert(3, miles).
=ERROR REPORT==== 28-May-2003::18:36:27 ===
Error in process <0.25.0> with exit value:
{function_clause,[{tut2,convert,[3,miles]},{erl_eval,expr,3},{erl_eval,expr
s,4},{shell,eval_loop,2}]}
exited: {function_clause,[{tut2,convert,[3,miles]},
{erl_eval,expr,3},
{erl_eval,exprs,4},
{shell,eval_loop,2}]}

convert函数的两个部分都调用了它的句话。在这里我们看到”miles”不在句话中。
Erlang系统无法匹配(match)任一个句话,所以我们得到一个错误信息
function_clause。

3、元组以”{“和”}”括起来的,所以我们可以写{inch, 3}来表示3英寸,并以{centimeter, 5}来表示5厘米。现在让我们写一个新的程序,将厘米转换成英寸,反之亦然(文件名:tut3.erl)。
-module(tut3).
-export([convert_length/1]).
convert_length({centimeter, X}) ->
{inch, X / 2.54};
convert_length({inch, Y}) ->
{centimeter, Y * 2.54}.
14> c(tut3).
{ok,tut3}
15> tut3:convert_length({inch, 5}).
{centimeter,12.7000}
16> tut3:convert_length(tut3:convert_length({inch, 5})).
{inch,5.00000}
注意16行。我们将5英寸转换为厘米,并把它转换回原来的值。此例说明,函数的参数也
可以是另一个函数的返回值。稍停片刻,来想一下16行的代码是如何工作的。我们给函数
的参数{inch, 5},是第一个与convert_length第一句话匹配的参数。如:
convert_length({centimeter, X})我们认为{centimeter, X}与{inch, 5}不,
匹配失败了,我们找下一个句话。如,convert_length({inch, Y}),Y得到5以后,
就匹配了。
我们看到上面的元组有两部分组成,不过元组可以有很多部分,我们想要多就都可以。如,
提供一个世界上不同城市温度值,我们可以这个写。
{moscow, {c, -10}}
{cape_town, {f, 70}}
{paris, {f, 28}}
一组元组有一个固定的大小。我们称元组中的东西为‘元素’。所以在元组{moscow,{c,-
10}}中,元素1是moscow,元素2是{c, -10}。我已经把c的意思确定为摄氏度
(Centigrade or Celsius),f确定为华氏度(Fahrenheit)。

4、鉴于元组把东西组到一起,我们也希望提供一个列表一样的东西。列表在Erlang中被括
在”[“和”]”里。如一个不同城市温度的列表可能是这样的:
[{moscow, {c, -10}}, {cape_town, {f, 70}}, {stockholm, {c, -4}},
{paris, {f, 28}}, {london, {f, 36}}]
一个很有用的遍历列表的方法是使用”|”。
18> [First |TheRest] = [1,2,3,4,5].
[1,2,3,4,5]
19> First.
1
20> TheRest.
[2,3,4,5]
25> [A, B | C] = [1, 2].
[1,2]
26> A.
1
27> B.
2
28> C.
[]
下面的例子告诉我们如何查出列表的长度:
-module(tut4).
-export([list_length/1]).
list_length([]) ->
0;
list_length([First | Rest]) ->
1 + list_length(Rest).
29> c(tut4).
{ok,tut4}
30> tut4:list_length([1,2,3,4,5,6,7]).
7

5、Erlang并没有字符串类型,取而代之我们可以提供一个由ASCII字符组成的列表。所以列表[97,98,99]相当于”abc”。Erlang shell很“慧明”,它会推测我们使用列表的意思,并以最接近形式输出
31> [97,98,99].
“abc”

6、io:format,用以标准输出
32> io:format(“hello world~n”, []).
hello world
ok
33> io:format(“this outputs one Erlang term: ~w~n”, [hello]).
this outputs one Erlang term: hello
ok
34> io:format(“this outputs two Erlang terms: ~w~w~n”, [hello, world]).
this outputs two Erlang terms: helloworld
ok
35> io:format(“this outputs two Erlang terms: ~w ~w~n”, [hello, world]).
this outputs two Erlang terms: hello world
ok
复杂一点的例子:
%% 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]).

36> c(tut5).
{ok,tut5}
37> 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.1111 c
stockholm -4 c
paris -2.22222 c
london 2.22222 c
ok
要注意-export([format_temps/1]).一行只包含format_temps/1函数,另一个函数是局部(local)函数,他们无法被tut5模块以外的东西访问。
print_temp简单的调用io:format,和我们上面所描述的一样。注意~-15w说明以15做为栏位长度打印,并左对齐。

7、取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 ->
New_result_far=Head
list_max(Rest, New_result_far);
list_max([Head|Rest], Result_so_far) ->
list_max(Rest, Result_so_far).
39> c(tut6).
{ok,tut6}
40> tut6:list_max([1,2,3,4,5,7,4,3,2,1]).
7
首先注意我们这里有两个同名的函数list_max。虽然每个都带有不同数据的参数。在Erlang这些都会被认为是完全不同的函数。我们需要通过“名称/参数数量”区分这些我们写的函数,比如在这里是list_max/1和list_max/2。
在list_max/2中,遍历列表,当Head > Result_so_far的时候,使用Head代替Result_so_far。在->之前的特定字符,表示--我们只会在这个特定的条件满足的时候,才会执行函数的这个部分。我们叫这种类型的测试,叫守卫(guard)。如果守卫不是‘真’(我们称它为守卫失败),我们会尝试执行函数的下一个部分。这种情况下如果Head不大于Result_so_far的话,它必定是小于等于它,所以我们在下一部分中,不需要再使用守卫。
一些守卫中常见的操作符有< 小于,> 大于,== 等于,>= 大于等于,=< 小于等于,/=不等于。
把上面的程序换成在列表中找最小值的程序,我们只需要把<替换成>。(不过最好也把函数的名字,改为list_min:-))。

还记得我前面所提到的--一个变量在它的作用域中只能做一次赋值吗?如上面所看到的,如:Result_so_far给赋了很多值。那是因为,我们每次调用list_max/2的时候,我们建立了一个新的作用域,Result_so_far在这个作用域中,都会被认为是完全不同的变量。另一个建立和给一个变量赋值的方法是使用 =。如果我写M = 5,那么一个名为M的变量会被创建,并赋于5这个值。如果在同一下作用域下,当我写M = 6,它就会发生错误。

41> M = 5.
5
42> M = 6.
exited: {
{badmatch,6},[{erl_eval,expr,3}]
}

43> M = M + 1.
exited: {
{badmatch,6},[{erl_eval,expr,3}]
}

44> N = M + 1.
6
匹配操作符在通过一组元素创建新变量的时候很有用。
45> {X, Y} = {paris, {f, 28}}.
{paris,{f,28}}
46> X.
paris
47> Y.
{f,28}
这里我们看到X的值被赋于paris,而Y是{f,28}。

8、更多关于列表
|可以用来得到列表中的第一个元素,|也可以用来在列表的头部添加元素:
53> L1 = [madrid | T1].
[madrid,london,rome]
54> L1.
[madrid,london,rome]
现在举一个使用列表的例子--反转列表:
-module(tut8).
-export([reverse/1]).
reverse(List) ->
reverse(List, []).
reverse([Head | Rest], Reversed_List) ->
reverse(Rest, [Head | Reversed_List]);
reverse([], Reversed_List) ->
Reversed_List.
56> c(tut8).
{ok,tut8}
57> tut8:reverse([1,2,3]).
[3,2,1]

9、If和Case
If这样工作的
if
Condition 1 ->
Action 1;
Condition 2 ->
Action 2;
Condition 3 ->
Action 3;
Condition 4 ->
Action 4
end
注意,在结尾没有”;”!条件(Conditions)和守卫一样,测试成功或是失败。Erlang会从最顶上开始向下,直到找到一个成功的条件为止,并执行行条件下面的动作,并且乎略掉其后面它的条件。如果没有条件匹配,会发生一个运行时(run-time)错误。元子true在条件中表示真,它常常被放在条件层,表示如果没有匹配的条件的话,应该做什么动作。

一个if的例子,蛋疼的if,没有true条件会报错
-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.
64> c(tut9).
{ok,tut9}
65> tut9:test_if(5,33).
A = 5
a_equals_5
66> tut9:test_if(33,6).
B = 6
b_equals_6
67> tut9:test_if(2, 3).
A == 2, B == 3
a_equals_2_b_equals_3
68> tut9:test_if(1, 33).
A == 1 ; B == 7
a_equals_1_or_b_equals_7
69> tut9:test_if(33, 7).
A == 1 ; B == 7
a_equals_1_or_b_equals_7
70> tut9:test_if(33, 33).
=ERROR REPORT==== 11-Jun-2003::14:03:43 ===
Error in process <0.85.0> with exit value:
{if_clause,[{tut9,test_if,2},{erl_eval,exprs,4},{shell,eval_loop,2}]}
exited: {if_clause,[{tut9,test_if,2},
{erl_eval,exprs,4},
{shell,eval_loop,2}]}

case是另一个Erlang结构:
-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.
71> c(tut10).
{ok,tut10}
72> tut10:convert_length({inch, 6}).
{centimeter,15.2400}
73> tut10:convert_length({centimeter, 2.5}).
{inch,0.98425}
注意,case和if都有返回值,case的行为也可以使用守卫来代替。可能需要一个例子来澄清这一点。下面的例子告诉我们一个给定年份的某月份的长度。我们当然需要知道年份,在闰年中,2月有29天。
-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.
74> c(tut11).
{ok,tut11}
75> tut11:month_length(2004, feb).
29
76> tut11:month_length(2003, feb).
28
77> tut11:month_length(1947, aug).
31