Elixir-散列表、散列字典、关键字列表、集合与结构体

字典

散列表和散列字典都实现了 Dict 的行为。

关键字模块也实现了,但是关键字列表允许出现重复的值,但是要用 Keyword 模块才能访问。

1
2
3
defmodule Demo do
def values(dict), do: dict |> Dict.values |> Enum.sum
end
1
2
3
4
5
iex> hd = [one: 1,two: 2,three: 3] |> Enum.into HashDict.new
#HashDict<[two: 2, one: 1, three: 3]>
iex> IO.puts Demo.values(hd)
6
:ok

其他kpi类似
Dict.get(kw_list, :likes) 获取
Dict.drop(hashdict, [:where, :likes]) 移除
Dict.put(hashdict, :also_likes, "Ruby") 增加
Dict.merge(map, hashdict) 合并
等等..

散列表模式匹配和更新

散列表

格式: %{_key, _value}

模式匹配

匹配代码类似如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
iex(1)> people = [
...(1)>
...(1)> %{ name: "jack", height: 188},
...(1)>
...(1)> %{ name: "rose", height: 168},
...(1)>
...(1)> %{ name: "tom", height: 185},
...(1)>
...(1)> %{ name: "jetty", height: 20},
...(1)>
...(1)> %{ name: "lucy", height: 170}
...(1)>
...(1)> ]
[%{height: 188, name: "jack"}, %{height: 168, name: "rose"},
%{height: 185, name: "tom"}, %{height: 20, name: "jetty"},
%{height: 170, name: "lucy"}]
iex(2)> for person = %{ height: height} <- people,
...(2)> height > 180,
...(2)> do: IO.inspect person
%{height: 188, name: "jack"}
%{height: 185, name: "tom"}
[%{height: 188, name: "jack"}, %{height: 185, name: "tom"}]

更新值

散列表是不可变的,所以更新后会产生新的列表。

更新散列表语法:
new_map = %{ old_map, | key => value, ...}

具体代码:

1
2
3
4
iex> m = %{a: 1, b: 2, c: 3}
%{a: 1, b: 2, c: 3}
iex> new_map = %{m | b: 3, c: 4}
%{a: 1, b: 3, c: 4}

使用结构体

当你想创建一个带类型的散列表,具有固定的字段集合以及与之对应的默认值的散列表,并且能像匹配内容一样进行模式匹配的时候,我们可以使用- 结构体defstruct

在模块内部使用 defstruct 宏来定义散列表的性质。

1
2
3
4
5
6
7
8
9
defmodule Demo do
#结构体
defstruct name: "", age: 18, allow_in: false

#是否能参加舞会
def may_attend_party(attendee = %Demo{}) do
attendee.age > 18 && attendee.allow_in
end
end
1
2
3
4
iex> jack = %Demo{name: "jack", age: 19, allow_in: true}
%Demo{age: 19, allow_in: true, name: "jack"}
iex> Demo.may_attend_party(jack)
true

另一种方式访问结构体

散列表能够通过 some_map[:name] 的方式访问,而我们前面的结构体是通过点符号。

由于散列表实现了 Access 协议(定义了使用方括号访问属性的能力),而结构体没有,所以我们可以通过添加简单的指令来使结构体拥有这项功能。

方式:@derive Access

改造下之前的代码:

1
2
3
4
5
6
7
8
9
10
defmodule Demo do
@derive Access
#结构体
defstruct name: "", age: 18, allow_in: false

#是否能参加舞会
def may_attend_party(attendee = %Demo{}) do
attendee.age > 18 && attendee.allow_in
end
end

然后你就可以通过方括号访问里面的属性了。

嵌套结构体

1
2
3
4
5
6
7
8
defmodule Customer do
defstruct name: "", company: ""
end

#嵌套ower结构体
defmodule BugReport do
defstruct owner: %{}, details: "", severity: 1
end
1
2
3
4
5
6
7
iex> report = %BugReport{owner: %Customer{name: "Dave", company: "Pragmatic"}, details: "broken"}
```
`owner` 属性本身也是一个机构体。

可以通过点符号访问嵌套字段:
```Elixir
iex> report.owner.company

但是更新时候特别麻烦,我们可以用 Elixir 的嵌套字典访问函数。

put_in 可以设定嵌套结构里面的值。

1
put_in(report.owner.company, "PragProg")

update_in 可以让我们在结构体的某个值上执行一个函数。

1
update_in(report.owner.name, &("Mr." <> &1))

另外还有两个嵌套函数,get_inget_and_update_in,可以访问iex 的文档获得更多资料。

动态嵌套访问

get_in, put_in, 等函数都接收一个键列表作为单独的参数,动态获取亲淘结构体中的字段。

类似如下:

1
IO.inspect get_in(person, [:son, :son, :name])

获取孙子的名字。 : )

put_in 类似

1
IO.inspect put_in(person, [:son, :son, :name], "shadan")

给孙子取个名字。 : )

HashSet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
iex> set1 = Enum.into 1..5, HashSet.new
#HashSet<[2, 3, 4, 1, 5]>
iex> Set.member? set1,3 #是否在set里
true
iex> Set.member? set1,8
false
iex> set2 = Enum.into 3..8, HashSet.new
#HashSet<[7, 6, 3, 4, 5, 8]>
iex> Set.union set1, set2 #合并
#HashSet<[7, 6, 2, 3, 4, 5, 1, 8]>
iex> Set.difference set1,set2 #set相对于set2的不同
#HashSet<[2, 1]>
iex> Set.difference set2,set1 #set2相对于set1的不同
#HashSet<[7, 6, 8]>
iex> Set.intersection set2,set1 #交叉部分
#HashSet<[3, 4, 5]>
iex>

而已看出 Set 并为维护顺序。

忘记对象的思想

记住 Elixir 是门函数式的编程语言。

当用到 defstruct 的时候,有面向对象编程经验的人会觉得这不就是 的概念么?

但是,请忘记过去面向对象的思想,做一个纯粹的开发。

看官赏点饭钱可好~