安装Elixir

安装教程

Shell

终端中键入iex/iex.bat进入 Shell

键入两次Ctrl + C 退出Shell

Shell中可以非常方便的练习和测试代码

使用Elixir执行脚本

1
2
# hello.exs
IO.puts("Hello Wrold.")

elixir hello.exs 执行脚本文件

其他命令

1
2
3
4
5
6
7
h trunc/1 # 查看 trunc/1 的帮助

# 一般情况下应该用该形式查询
# 内核模块是自动导入所以可以不需要模块名
h Kernel.trunc/1

i "hello" # 查看值信息

基本类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1       # 整型
0x1F # 整型
1.0 # 浮点
true # 布尔
:atom # 原子 / 符号 (代表本身的常量)
"xxx" # 字符串(二进制串)
[1,2,3] # 列表
{1,2,3} # 元组

# 进制
0b1010 # 10 2进制
0o777 # 511 8进制
0x1F # 31 16进制

1.0e-10 # 科学计数法

基础运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 数字
1 + 2 # 3
5 * 5 # 15
10 / 2 # 5.0
div(10, 2) # 5 整除
div 10, 2 # 5 可以不用括号
rem 10, 3 # 1 余数
round 3.5 # 4 四舍五入
trunc 3.5 # 3 截取整数

# 列表
[1] ++ [2] # [1,2] 连接列表
[1] -- [1] # []

# 字符串
"foo" <> "bar" # "foobar"

逻辑运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# and or not 第一个值必须是布尔值
true and false
true or raise("")
false and raise("")
not true

# || && ! 接受任何值,除了 false 和 nil 都是 true
1 || 2 # 1
nil && 1 # nil
! 1 # false

# == != === !== >= <= < >
1 == 1 # true
1 != 1 # false
1 == 1.0 # true
1 === 1.0 # false

不同类型也可以进行比较 number < atom < reference < function < port < pid < tuple < map < list < bitstring

注意在map中,会按键升序比较值,同时整型<浮点

字符串

这里的字符串是utf-8编码的二进制位串,注意与列表区分

1
2
3
4
5
6
7
8
9
10
11
12
# 字符串插值
var = :world
"hello #{var}" # hello world
"hello
world" # hello\nworld 可以直接使用换行
"hello\nworld" # 也可以使用转义字符
IO.puts("hello world") # 打印字符串

s = "hellö"
is_binary(s) # true
byte_size(s) # 6 占用字节
String.length(s) # 5 字符串长度

列表

1
2
3
4
5
6
7
8
# 可能也会表现出字符串的形式,实际上是列表
[104, 101, 108, 108, 111]
'hello' # otp 26 前的写法 单引号
~c"hello"

l = [1,2,3]
hd(l) # 1 取头元素
tl(l) # [2,3] 取尾列表

元组

1
2
3
4
5
# 下标从0开始
t = {:ok, "hello"}
tuple_size(t) # 2
elem(t, 1) # "hello"
put_elem(t, 1, "world") # {:ok, "world"}

使用列表还是元组

列表是以链表的形式存储,所以当连接两个列表时候,需要遍历左边的列表找到尾节点连接

元组是使用连续的内存,可以很方便的访问元素,但是更新或者添加元素会导致重新创建并生成新的元组

但是新元组中的元素不会每次重新创建,会共享未更改的内容以节省内存,元组和列表中的元素也会共享。也只有不可变的特性才可以实现

在计算结构中元素个数时候,如果是常量时间内就能得到结果的则命名为size,如果是线性时间内得到结果的命名为length。例如:tuple_size, byte_size; length, String.length

类型判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
is_boolean(true)    # true
is_boolean(1) # 除了 true 都是 false

is_integer(1)
is_float(1.0)
is_number(1)

# true 和 false 也是 atom
# true 就是 :true
# 大写开头的变量名也是 atom 如: Hello
is_atom(:atom)

is_function(add) # 是否是一个函数
is_function(add, 1) # 是否是只有一个参数的函数

匿名函数

1
2
add = fn a, b -> a + b end  # 定义一个匿名函数
add.(1, 2) # 调用匿名函数 注意有个.

模式匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
x = 1       # 赋值
1 = x # 匹配比较 1 = 1 没问题
2 = x # 此时会报错 2 = 1 报错
x = 2 # 重新赋值
2 = x # 匹配 2 = 2 没问题
^x = 1 # 强制匹配 2 = 1 报错

# 可以很方便的取出元组的值,个数必须对应
{a, b, c} = {1, 2, 3}

# 当然列表也是可以的
[a, b, c] = [1, 2, 3]
[h | t] = [1, 2, 3] # h = 1, t = [2, 3]

# 如果不关心其中一些值,可以使用 _
[h | _] = [1, 2, 3] # h = 1

模式匹配的左侧不允许进行函数调用

控制流

case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
case {1, 2, 3} do
{4, 5, 6} -> "1"
{1, x, 3} -> "2"
_ -> "3"
end
# 2 会匹配到第二个,同时 x 会被赋值 2

# 当然可以使用强制匹配,额外的条件限制
# 子句中的判断不会发生报错,只会匹配失败
x = 1
case 10 of
^x -> "1"
y when y < 1 -> "2"
y when hd(y) -> "3"
_ -> "4"
end
# 4

# 匿名函数也可以使用,注意保持所有参数个数一致
f = fn
x, y when x > 0 -> x + y
x, y -> x * Y
end

cond

1
2
3
4
5
6
7
# 类似 if else if 注意这里除了 nil 和 false 都是true
cond do
2 + 1 == 3 -> "1"
3 + 1 == 4 -> "2"
true -> "2"
end
# 2

ifunless

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# if 条件是 nil/false 则不执行同时返回 nil, unless 则相反
if true do
"1"
end
# 1

unless true do
"1"
end
nil

# 也都支持 else
if false do
"1"
else
"2"
end
# 2

注意在控制流中无法改变外面的变量,需要返回进行重新赋值

二进制型、字符串(位串)和字符列表*

UnicodeUTF-8

Unicode包含了所有字符,每个字符都有对应的码点

UTF-8是一种使用二进制存储Unicode码点的编码,是一种可变长编码

1
2
3
4
5
6
7
8
9
10
11
string = "héllo"
String.length(string) # 5 有5个字符
byte_size(string) # 6 占6个字节

# 可以使用 ? 查看字符对应的码点
?a # 97

# 可连接一个 <<0>> 查看对应的二进制位串
"hełło" <> <<0>> # <<104, 101, 197, 130, 197, 130, 111, 0>>
# 或者使用 IO.inspect/2 查看
IO.inspect("hełło", binaries: :as_binaries) # <<104, 101, 197, 130, 197, 130, 111>>

二进制型和位串