“Progarmming Elixir Note”的版本间的差异
Dennis zhuang(讨论 | 贡献) |
Dennis zhuang(讨论 | 贡献) (→类型和基本运算符) |
||
(未显示1个用户的13个中间版本) | |||
第1行: | 第1行: | ||
+ | == shell == | ||
+ | iex 同样支持 ctrl + G ,进入 erlang 用户命令相关菜单,也就是任务控制台,非常有用。 | ||
+ | == 类型和基本运算符 == | ||
+ | |||
+ | 小于等于终于是 <=,而不是 Erlang 的 =< | ||
+ | |||
+ | <code>== </code>表示数值意义上的相等,因此 <code>2==2.0 </code>返回 true,而 <code>=== </code>会完全匹配,所以 <code> 2=== 2.0 </code>返回 false,对应 erlang 就是 <code>== </code>和 =:= 。 | ||
+ | |||
+ | A String in Elixir is a UTF-8 encoded binary,非常关键,字符串其实是 binary。但是单引号的 'abc' 其实是 char list,也就是 list,iex 当发现一个 list 内的元素都是可打印的字符的时候,会输出字符串形式: | ||
+ | |||
+ | <pre> | ||
+ | iex(5)> [97,98,99] | ||
+ | 'abc' | ||
+ | </pre> | ||
+ | |||
+ | 如果你要强迫他输出原始格式,可以在前面加个 '0': | ||
+ | |||
+ | <pre> | ||
+ | iex(6)> a = [97,98,99] | ||
+ | 'abc' | ||
+ | iex(7)> ['0' |a] | ||
+ | ['0', 97, 98, 99] | ||
+ | </pre> | ||
+ | |||
+ | 这是一个比较常见的技巧。 | ||
+ | |||
+ | 各种类型的内存大小和限制参考 http://www.erlang.org/doc/efficiency_guide/advanced.html | ||
== Dictionaries == | == Dictionaries == | ||
第72行: | 第99行: | ||
%Subscriber{name: "green", over_18: true, paid: false} | %Subscriber{name: "green", over_18: true, paid: false} | ||
</pre> | </pre> | ||
+ | |||
+ | 嵌套 struct,可以使用 put_in, update_in 来修改: | ||
+ | |||
+ | <pre> | ||
+ | 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)> | ||
+ | </pre> | ||
+ | |||
+ | === 嵌套访问 Map 或者 Keyword list === | ||
+ | |||
+ | 也可以用 put_in, update_in, get_and_update_in 等。 | ||
+ | |||
+ | <pre> | ||
+ | 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} | ||
+ | </pre> | ||
+ | |||
+ | 但是上面见到的这些 put_in, update_in 都是宏,因此只能静态地传入固定的 key 列表,无法作为函数调用动态传入,也无法任意的 key 个数。因此他们还有函数的重载版本,增加一个 keys 列表: | ||
+ | |||
+ | <pre> | ||
+ | iex(87)> update_in(report,[:owner,:name], &("Mr. " <> &1)) | ||
+ | %{details: "test", owner: %{company: "leancloud", name: "Mr. dennis"}, | ||
+ | severity: 1} | ||
+ | </pre> | ||
+ | |||
+ | P.S. 其实这些都是类似 clojure 了。 | ||
+ | |||
+ | === get_in 技巧 === | ||
+ | |||
+ | get_in 和 get_and_update_in 的 key 如果是一个函数,这个函数将被调用来返回对应的值,函数还可以传递给下一个 key(或者函数): | ||
+ | |||
+ | <pre> | ||
+ | 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"] | ||
+ | </pre> | ||
+ | |||
+ | == 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: | ||
+ | |||
+ | <pre> | ||
+ | iex --name two | ||
+ | > :global.register_name "two", :erlang.group_leader | ||
+ | </pre> | ||
+ | |||
+ | 打开 one node: | ||
+ | |||
+ | <pre> | ||
+ | iex --name one | ||
+ | > Node.connect :"two@dennis-macbook.local" | ||
+ | > two = :global.whereis_name "two" | ||
+ | > IO.puts two, "hello" | ||
+ | </pre> | ||
+ | |||
+ | 在 node two 会输出 hello,相当酷。 | ||
+ | |||
+ | == OTP Servers == | ||
+ | |||
+ | OTP designs system in terms of hierarchies of applications. |
2015年11月22日 (日) 08:50的最后版本
目录 |
[编辑] shell
iex 同样支持 ctrl + G ,进入 erlang 用户命令相关菜单,也就是任务控制台,非常有用。
[编辑] 类型和基本运算符
小于等于终于是 <=,而不是 Erlang 的 =<
==
表示数值意义上的相等,因此 2==2.0
返回 true,而 ===
会完全匹配,所以 2=== 2.0
返回 false,对应 erlang 就是 ==
和 =:= 。
A String in Elixir is a UTF-8 encoded binary,非常关键,字符串其实是 binary。但是单引号的 'abc' 其实是 char list,也就是 list,iex 当发现一个 list 内的元素都是可打印的字符的时候,会输出字符串形式:
iex(5)> [97,98,99] 'abc'
如果你要强迫他输出原始格式,可以在前面加个 '0':
iex(6)> a = [97,98,99] 'abc' iex(7)> ['0' |a] ['0', 97, 98, 99]
这是一个比较常见的技巧。
各种类型的内存大小和限制参考 http://www.erlang.org/doc/efficiency_guide/advanced.html
[编辑] 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.