Erlang 入门
基础
安装 Erlang Shell
-
命令安装
For Homebrew on OS X: brew install erlang
For MacPorts on OS X: port install erlang
For Ubuntu and Debian: apt-get install erlang
For Fedora: yum install erlang
For FreeBSD: pkg install erlang
基本使用
启动与退出
在终端中输入
erl
即可进入Erlang Shell
% 这是一个单行注释,Erlang 中没有多行注释
% Erlang 中 . 表示语句的结束 (类似 Java/C 语言中的 分号)
% Erlang Shell 类似 Python 的 IDLE,可以直接进行运算
% Erlang 为函数式语言,所有函数、表达式都有返回值,运行成功时都会返回一个 atom 类型的 ok
io:format("hello world!~n"). % 打印 hello world,~n 表示换行
1 + 1. % 输出 2
q(). % init():stop(). 的快捷方式,退出 Erlang Shell
变量
Erlang
是动态类型(声明变量时候不需要指定类型)同时也是强类型(Erlang 不会自动的进行类型转换)的语言
% pattern match (模式匹配)
A = 1. % 在 Erlang 中 = 称为模式匹配,后面会再讲
% 若 A 没有值,则会将右边的值绑定到 A,相当于赋值
% 若 A 有值且两边值相等,则匹配成功,否则报错
% 变量只有绑定才可以使用,虽然称之为变量,但实际是 'Immutable' (不可变的)
_Age = 2. % 下划线开头的变量,即使没有使用/绑定也没关系,编译也不会报错
函数
Add = fun(A, B) -> A + B end. % 声明一个匿名函数,需要两个参数,绑定到 Add 变量上
Add(1,2). % 调用
基本类型
Number
1 + 1. % 2
1 * 2. % 2
2 - 1. % 1
5 / 2. % 2.5
5 div 2. % 2, div 表示整除
5 rem 2. % 1 rem 表示取余
% 不同进制表示,格式:Base#Value,Base 默认为 10
2#10. % 表示二进制的 10,即十进制的 2
8#10. % 表示8进制的 10, 即十进制的 8
...
Atoms
可以理解为一个常量,atom
类型使用内存很小且运算速度很快,但注意数量是有限制的。
% 以下都是合法的 atom 类型
atom.
atom_rule.
atom_@123.
'Atom 123'. % 包含特殊字符需要写在单引号中
atom = 'atom'.
% true、false 就是一种 atom 类型
Tuples
元组是不用类型的值组成的一个类型。语法:{element1, element2, ..., elementN}
{1,2}.
% 一般情况下,会使用 atom 类型作为第一个值用于标注这个元组的含义,称之为 'tagged tuple'
{point, 1, 2}. % 表示一个点坐标
{rectangle, 2, 3}. % 表示一个矩形的长度和宽度
List
List
就是链表,可以包含各种类型的值
% 常用语法
[1, 2, 3].
[1, [2,3], {4}].
% Erlang 中 String 就是 List
"abc"
[97, 98, 99]. % 与上一行等效
% 操作符
[1, 2] -- [1]. % [2]
[1] ++ [2]. % [1, 2]
List = [1,2]
hd(List). % 1
tl(List). % 2
[0 | List]. % [0, 1, 2],头部插入一个 0
% List Comprehensions 语法, 可以根据需求生产新的 List
[2*N || N <- [1,2]]. % [2, 4], 将列表元素乘 2
[N || N <- [1,2], N rem 2 =:= 0]. % [2],取出列表中的偶数
Tips:
- 列表相对来说拼接等操作比较耗时,同时比较占内存,字符串可以使用内存更小的
Binary
类型,比如:<<"abc">>
Tuple
与List
最大的区别在于定长与不定长(个人理解)
运算符
逻辑运算符
true and false. % false,与 andalso 类似
true or false. % true, 与 orelse 类似
true xor false. % true
not false. % true
% and/or 和 andalso/orelse 区别在于后者右边的运算不一定会执行
比较运算符
1 < 2. % true
2 > 1. % true
1 =< 2. % true % 注意写法 =<
1 >= 2. % false
1 =:= 1. % true,严格相等
1 == 1.0. % true,大概相等
1 =/= 1. % false,严格不等
1 /= 2. % true,相差很多
% =:= 为判断相等,=/=判断不等(包括类型)
不同类型之间也存在大小关系:
number < atom < reference < fun < port < pid < tuple < list < bit string
Modules
定义与使用
% test.erl
-modules(test). % 定义模块,需要与文件名一致
-export([add/2, add/3]). % 指定导出的函数,未导出函数无法调用
% [function/paramNum], 存在同名函数但是参数个数不一样格式如上一行
add(A, B) -> A + B.
add(A, B, C) -> A + add(B + C).
当存在参数个数相同的函数用 分号 隔开
% test2.erl
-module(test2).
-export([add/2]).
% 当 A 和 B 都是数字时
add(A, B) when is_number(A), is_number(B) ->
A + B; % 此处分号隔开
% 当 A 和 B 都是列表时
add(A, B) when is_list(A), is_list(B) ->
A ++ B.
% 调用时会从上往下匹配,其余情况没有匹配不执行
单个文件的编译与运行
方法一
在 cmd
中编译 erlang 文件
:: 这是 cmd 中的注释,测试环境为 windows
mkdir ebin :: 创建一个文件夹 ebin
erlc -o ebin test.erl :: 编译 test.erl,将编译后文件放到 ebin 文件夹中
erl -pa ebin :: 将 ebin 文件夹加入 erlang beam 文件查找目录并启动 erlang shell
在 erlang shell
中调用函数
% 只有按照上面的操作后以下方法才会生效
test:add(1, 2). % 3
test:add(1, 2, 3). % 6
方法二
% 在 erl 文件所在的地方打开 cmd, `erl` 进入 erlang shell
c(test). % 编译后原地生成 beam 文件
test:add(1, 2). % 3
test:add(1, 2, 3). % 6
模式匹配
case clauses
类似其他语言中的
switch case
语句,通过匹配不同的条件选择不同分支
% test.erl
-modules(test).
-export([show/1]).
% 示例函数
show(Color) ->
case Color of
red -> io:fomat("So hot.");
blue -> io:fomat("So cool.");
_ -> io:fomat("So ...") % _ 是一个特殊的变量,单独使用可以匹配任何东西
end.
function clauses
% test.erl
-modules(test).
-export([show/1]).
% 示例函数
show(red) -> io:fomat("So hot.");
show(blue) -> io:fomat("So cool");
show(_) -> io:fomat("So ...").
匹配获取值
{X | 2} = {1, 2}. % X = 1
[X | Y] = [1, 2, 3] % X = 1, Y = [2,3]
[_ | X] = [1, 2] % X = 2
f(). % 解绑所有变量
When
% 18 岁才可以...
fun(X) when X >= 18 -> true;
fun(_) -> false.
% 14-18岁之间...
fun(X) when X <= 18, X >=14 -> true;
fun(_) -> false.
when 中
,
表示 and;
表示 or,可以使用andalso
和orels
Records
与
tagged tuple
类似,但是更加直观
% records.erl
-module(records).
-export([get_user_name/1,
get_user_phone/1]).
-record(user, {
name,
phone
}).
get_user_name(#user{name=Name}) ->
Name.
get_user_phone(#user{phone=Phone}) ->
Phone.
% 进入 erlang shell
c(records). % 编译文件
rr(records). % 将 record 加载到 shell 中
User = #user{name=<<"Jeson">>, phone=<<"123">>}. % 定义一个 record
Name = record:get_user_name(User). % Jeson,获取 User 中的名字
Phone = record:get_user_phone(User). % 123
User#user.name. % Jeson
#user.name. % 2,record 本质就是一个 tagged tuple,所有 name 是第二个元素
Erlang
中位置从 1 开始计算
递归
因为 Erlang
中没有迭代语句,所以使用递归解决大部分的问题
% 场景:计算一个 List 的长度
len([]) -> 0;
len([_|T]) -> 1 + len(T). % 递归计算长度
尾递归
上述递归存在一个问题,就是需要递归到最深处再回溯进行相加操作,对内存消耗较大,可以使用尾递归进行优化
len(T) -> len(T, 0). %调用尾递归函数
len([], Len) -> Len;
len([_|T], Len) -> len(T, Len+1).
制作一个简单的 GIF 演示一下:
并行
参考资料:简书