Erlang 入门 ---- 基础(一)

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 = ‘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">>
  • TupleList 最大的区别在于定长不定长(个人理解)

运算符

逻辑运算符
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,可以使用 andalsoorels

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 演示一下:

Erlang 入门 ---- 基础(一)

并行

参考资料:简书

相关推荐