📅  最后修改于: 2023-12-03 15:14:53.350000             🧑  作者: Mango
Erlang是一种面向并发的编程语言,特别适合构建分布式、实时、容错的应用程序。Erlang的语法类似于普通的函数式编程语言,如Scheme和Haskell,但它的并发处理、容错和错误恢复能力非常出色,因此适用于许多高并发和可靠性要求的应用场景。
在本教程中,我们将介绍Erlang的基本语法、数据类型、模块和函数、控制结构、并发模型以及错误处理等方面的内容。
安装Erlang可以访问官方网站https://www.erlang.org/downloads,选择适合自己系统的版本下载安装即可。
在Erlang中变量以大写字母开头,可以包含字母、数字、下划线。变量在声明时可以被赋值,但是之后不能改变它的值。
1> X = 1.
1
2> Y = 2.
2
3> X = 3.
** exception error: no match of right hand side value 3
Erlang的原子用于表示常量,通常用于状态变量、消息类型等常量表达。原子以小写字母或用单引号引起来的字母和数字组成。
1> ok.
ok
2> hello.
hello
3> 'Erlang is awesome'.
'Erlang is awesome'
函数调用的基本语法是:函数名(参数1, 参数2, ..., 参数n)。
1> io:fwrite("Hello ~s~n", ["Erlang"]).
Hello Erlang
ok
在Erlang中单行注释以%开头,多行注释用/.../括起来。
% 这是一行注释
%% 这是另一行注释
/*
这是多行注释
*/
Erlang支持整数和浮点数。
1> 123.
123
2> 3.14.
3.14
Erlang中的布尔值只有两个:true和false。
1> true.
true
2> false.
false
元组是有序的值集合,使用大括号{}括起来,用逗号分隔元素。
1> {1, 2, 3}.
{1,2,3}
2> {apple, 5.0, true}.
{apple,5.0,true}
列表是有序的值集合,使用中括号[]括起来,用逗号分隔元素。
1> [1, 2, 3].
[1,2,3]
2> [apple, 5.0, true].
[apple,5.0,true]
Erlang中的字符串实际上是由字符列表组成。
1> "Hello, Erlang!".
"H.e.l.l.o.,. .E.r.l.a.n.g.!."
Erlang中的二进制用<<>>括起来,用逗号分隔元素。
1> <<1, 2>>.
<<1,2>>
2> <<"Hello, Erlang!">>.
<<"Hello, Erlang!">>
在Erlang 17.0之后,新加入了映射(Map)数据类型。
1> Map = #{a => 1, b => 2}.
#{a => 1,b => 2}
2> maps:get(a, Map).
1
一个Erlang模块是一个包含函数的文件,文件名必须与模块名相同。每个模块由以下部分组成:
-module(hello_world).
-export([hello/0]).
hello() ->
io:fwrite("Hello, World!~n").
函数定义由函数名、参数列表和函数体组成。
-module(my_module).
-export([double/1]).
double(X) ->
X * 2.
上面的例子定义了一个名为double的函数,它接受一个参数X,返回X的两倍。
调用函数的基本语法是:模块名:函数名(参数1, 参数2, ..., 参数n)。
1> my_module:double(5).
10
在Erlang中,函数可以作为参数传递给其他函数,也可以作为返回值返回。
-module(higher_order).
-export([times/1, apply_twice/2]).
times(N) ->
fun(X) -> X * N end.
apply_twice(F, X) ->
F(F(X)).
上面的代码定义了一个高阶函数times,它返回一个将其参数N与传递给它的参数相乘的匿名函数,以及一个高阶函数apply_twice,它在传递给它的函数上调用两次。
1> Times3 = higher_order:times(3).
#Fun<higher_order.1.15632420>
2> Times3(5).
15
3> higher_order:apply_twice(Times3, 5).
45
if语句基本语法为:
if
Condition1 -> Action1;
Condition2 -> Action2;
true -> Action3
end.
Erlang支持多个条件选项,如果有多个条件选项,请注意在分号后面添加空格。
-module(if_demo).
-export([check_marks/1]).
check_marks(Marks) ->
if
Marks >= 80 -> io:fwrite("Grade A~n");
Marks >= 60 -> io:fwrite("Grade B~n");
Marks >= 40 -> io:fwrite("Grade C~n");
true -> io:fwrite("Grade F~n")
end.
case语句比if语句更为灵活,可以在不同的情况下执行不同的操作。case语句的基本语法为:
case Expression of
Pattern1 [when Guard1] -> Action1;
Pattern2 [when Guard2] -> Action2;
...
PatternN [when GuardN] -> ActionN
end.
Erlang允许在模式中使用变量和常量,如果需要可添加when语句来进一步筛选。
-module(case_demo).
-export([check_number/1]).
check_number(X) ->
case X of
0 -> io:fwrite("Number is zero~n");
1 -> io:fwrite("Number is one~n");
2 -> io:fwrite("Number is two~n");
_ -> io:fwrite("Unknown number~n")
end.
Erlang中有三种循环语句:
for循环允许在已知的循环次数内执行一个代码块。例如,我们可以使用for循环来计算一个数字列表的总和:
-module(for_loop).
-export([sum_list/1]).
sum_list(List) ->
Length = length(List),
Sum = for(I = 1, I =< Length, 0) do
lists:nth(I, List)
end,
Sum.
while循环允许在某个条件保持为真的情况下无限循环执行一个代码块,直到条件不再为真。
-module(while_loop).
-export([countdown/1]).
countdown(X) when X > 0 ->
io:fwrite("~w~n", [X]),
countdown(X - 1);
countdown(0) ->
io:fwrite("Blast off!~n").
递归允许在函数内部无限循环调用自身。
-module(recursion).
-export([sum_list/1]).
sum_list([]) ->
0;
sum_list([H | T]) ->
H + sum_list(T).
Erlang的并发性是通过它的Actor模型实现的,每个Actor代表一个独立的并发执行单元,并且不会与其他Actor共享内存或直接相互通信。相反,它们通过异步消息传递进行通信。
以下是一个可以避免死锁的例子代码:
-module(concurrency).
-export([start/0, func1/1, func2/1]).
start() ->
Pid1 = spawn(fun() -> func1([]) end),
Pid2 = spawn(fun() -> func2([]) end),
timer:sleep(300),
Pid2 ! {self(), stop}.
func1(List) ->
receive
{From, add, Item} ->
NewList = lists:append(List, [Item]),
From ! {self(), ack},
func1(NewList);
{From, get} ->
From ! {self(), list, List},
func1(List);
{_, stop} ->
ok
end.
func2(List) ->
receive
{From, add, Item} ->
NewList = lists:append(List, [Item]),
From ! {self(), ack},
func2(NewList);
{From, get} ->
From ! {self(), list, List},
func2(List);
{_, stop} ->
ok
end.
spawn函数用于在新进程中启动一个函数。
1> Pid = spawn(fun() -> io:fwrite("Hello, World!~n") end).
Hello, World!
<0.41.0>
这个函数在一个新进程中执行,我们可以使用pid来与它通信,例如发送消息或停止它。
send函数用于向另一个进程发送消息。
1> Pid ! {self(), add, 5}.
{<0.36.0>,ack}
2> Pid ! {self(), get}.
{<0.36.0>,list,[5]}
receive函数用于从进程接收消息。
-module(receive_demo).
-export([start/0, func/0]).
start() ->
Pid = spawn(fun() -> func() end),
Pid ! {self(), 5},
Pid ! {self(), 7},
Pid ! {self(), stop}.
func() ->
receive
{From, X} ->
io:fwrite("Received ~w~n", [X]),
func();
{From, stop} ->
ok
end.
Erlang中可以通过try-catch或通过返回特殊的{error, Reason}元组来处理错误。
try-catch结构用于捕获和处理异常。
1> try 1/0 catch _:_ -> io:fwrite("Error~n") end.
Error
ok
在Erlang中,可以通过返回的元组中添加{error, Reason}来指示错误。
-module(error_handling).
-export([function_that_can_fail/1]).
function_that_can_fail(X) ->
if X < 0 -> {error, "Negative number not allowed"};
X > 10 -> {error, "Number too large"};
true -> X * 2
end.
在调用此函数时,您可以使用模式匹配来检查是否有错误。
1> case error_handling:function_that_can_fail(-1) of
{error, Reason} ->
io:fwrite("Error: ~w~n", [Reason]);
Result ->
io:fwrite("Result: ~w~n", [Result])
end.
Error: Negative number not allowed
ok