“Progarmming Elixir Note”的版本间的差异

来自Dennis的知识库
跳转到: 导航搜索
第171行: 第171行:
  
 
在 node two 会输出 hello,相当酷。
 
在 node two 会输出 hello,相当酷。
 +
 +
== OTP Servers ==
 +
 +
OTP designs system in terms of hierarchies of applications.

2015年11月9日 (一) 14:37的版本


目录

Dictionaries

  • 一个项对应多个同个 key: Keyword 模块
  • 要求顺序: Keyword 模块
  • 使用模式匹配内容:Map
  • 需要保存很多项:HashDict,Map 性能较差。


Keyword 模块示例

iex(21)> kw_list = [name: "Dave", likes: "Elixir", likes: "Ruby", where: "Beijing"]
[name: "Dave", likes: "Elixir", likes: "Ruby", where: "Beijing"]
iex(22)> kw_list[:likes]
"Elixir"
iex(23)> Dict.get(kw_list, :likes)
"Elixir"
iex(24)> Keyword.get(kw_list, :likes)
"Elixir"
iex(25)> Keyword.get_values(kw_list, :likes)
["Elixir", "Ruby"]
iex(26)> Keyword.get_values(kw_list, :likes)
["Elixir", "Ruby"]
iex(27)> 

get_values 用来获取多个相同 key 的值。

Map

  • 对于已经存在的 key,可以用 | 来更新,但是无法添加新 key:
iex(35)> person = %{name: "dennis", height: 1.7}
%{height: 1.7, name: "dennis"}
iex(36)> %{person | name: "green"}
%{height: 1.7, name: "green"}
iex(37)> %{person | age: 32}
** (KeyError) key :age not found in: %{height: 1.7, name: "dennis"}
    (stdlib) :maps.update(:age, 32, %{height: 1.7, name: "dennis"})
    (stdlib) erl_eval.erl:255: anonymous fn/2 in :erl_eval.expr/5
    (stdlib) lists.erl:1262: :lists.foldl/3

必须用 Dict.put_new/3 来添加新 key:

iex(37)> Dict.put_new(person, :age, 3)
%{age: 3, height: 1.7, name: "dennis"}

Structs

其实就是固定 key 的 Map,但是却没有 Dict 和 Access 访问协议,并且只能定义在 module。在后续的 protocol 多态中扮演重要角色。

Structs 当然可以嵌套。 Structs 已经无法继承 @derive Access 协议,elixir 1.1 版本废弃了 Access 协议。Struct 本质上是 Map,可以使用模式匹配、Map 模块 API 等:

defmodule Subscriber do
  defstruct name: "", paid: false, over_18: true
end

iex(55)>  s1 = %Subscriber{name: "dennis"}
%Subscriber{name: "dennis", over_18: true, paid: false}
iex(56)> %{s1 | name: "Green"}
%Subscriber{name: "Green", over_18: true, paid: false}
iex(57)> Map.get(s1, :name)
"dennis"
iex(58)> Map.put(s1, :name, "green")
%Subscriber{name: "green", over_18: true, paid: false}

嵌套 struct,可以使用 put_in, update_in 来修改:

defmodule Customer do
	defstruct name: "", company: ""
end

defmodule BugReport do
	defstruct owner: %{}, details: "", severity: 1
end

iex(77)> report = %BugReport{details: "test", owner: %Customer{name: "dennis", company: "leancloud"}}
%BugReport{details: "test",
 owner: %Customer{company: "leancloud", name: "dennis"}, severity: 1}
iex(78)> report
%BugReport{details: "test",
 owner: %Customer{company: "leancloud", name: "dennis"}, severity: 1}
iex(79)> report.owner.name
"dennis"
iex(80)> report.owner.company
"leancloud"
iex(81)> put_in(report.owner.name, "green")
%BugReport{details: "test",
 owner: %Customer{company: "leancloud", name: "green"}, severity: 1}
iex(82)> update_in(report.owner.name, &("Mr." <> &1))
%BugReport{details: "test",
 owner: %Customer{company: "leancloud", name: "Mr.dennis"}, severity: 1}
iex(83)> 

嵌套访问 Map 或者 Keyword list

也可以用 put_in, update_in, get_and_update_in 等。

iex(84)> report = %{details: "test",
 owner: %{company: "leancloud", name: "dennis"}, severity: 1}
...(84)> %{details: "test", owner: %{company: "leancloud", name: "dennis"}, severity: 1}
iex(85)> put_in(report[:owner][:name], "green")
%{details: "test", owner: %{company: "leancloud", name: "green"}, severity: 1}

但是上面见到的这些 put_in, update_in 都是宏,因此只能静态地传入固定的 key 列表,无法作为函数调用动态传入,也无法任意的 key 个数。因此他们还有函数的重载版本,增加一个 keys 列表:

iex(87)> update_in(report,[:owner,:name], &("Mr. " <> &1))
%{details: "test", owner: %{company: "leancloud", name: "Mr. dennis"},
  severity: 1}

P.S. 其实这些都是类似 clojure 了。

get_in 技巧

get_in 和 get_and_update_in 的 key 如果是一个函数,这个函数将被调用来返回对应的值,函数还可以传递给下一个 key(或者函数):

authors = [
  %{name: "Jose", language: "Elixir"},
  %{name: "Matz", language: "Ruby"},
  %{name: "Larry", language: "Perl"}
]

languages_with_an_r = fn(:get, collection, next_fn) ->
  for row <- collection do
    if String.contains?(row.language, "r"), do: next_fn.(row)
  end
end

iex(95)> get_in(authors, [languages_with_an_r, :name])
["Jose", nil, "Larry"]

Enum 和 Stream

Enum 就是类似 clojure 的 sequence 抽象,提供各种高阶函数,map,filter,reduce,sort, every?,any? 等等,但是他不是 lazy 的,而 Stream 是 Lazy 的, Stream 的概念更像 clojure 的 transducer,但不完全是,详细参考作者的文章 http://blog.plataformatec.com.br/2015/05/introducing-reducees/

erlang.group_leader

打开的文件设备在 erlang 里表示为一个 pid,默认的 device 也就是 stdout 就是 :erlang.group_leader,你可以将他注册到全局,其他 erl 节点就可以向这个设备打印消息,试验:

打开 two node:

iex --name two
> :global.register_name "two", :erlang.group_leader

打开 one node:

iex --name one
> Node.connect :"two@dennis-macbook.local"
> two = :global.whereis_name "two"
> IO.puts two, "hello"

在 node two 会输出 hello,相当酷。

OTP Servers

OTP designs system in terms of hierarchies of applications.

个人工具
名字空间

变换
操作
导航
工具箱