声明:本文讨论的Erlang Maps是基于17.0-rc2,时间2014-3-4.兴许Maps可能会出现语法或函数API上的有所调整,特此说明.

前情提要: [Erlang 0116] 当我们谈论Erlang Maps时,我们谈论什么 Part 1

继续昨天的话题,在Erlang Factory SF Bay Area 2013有一个议题:"Where are we on the Map?" [PDF ],这个Talk基本上就是选取了EEP43的要点,有兴趣的同学能够FQ观看视频 Where are We on the Map?

- Kenneth Lundin - YouTube 假设是腿脚不利索的,能够看墙内的.细致阅读EEP43,其信息量巨大,包括Maps的设计演变来龙去脉,各种取舍,也是我们学习设计的极佳范例.以下我将依照自己的逻辑顺序又一次解读EEP43,先从怎样使用開始,直观上感受一下区别,然后再回答"何必有我"的问题.

Maps Basic

EEP43 给出了Map比較规范的定义, Map M包括一定数量的键值对,实现从K1..Kn到V1..Vn的映射,当中没有两个Key是相等的(equal). equal指的是K1==K2,matching指的是K1 =:= K2. erlang:is_map(M)用于推断数据是否map类型.只是依照如今的情况,当出现1.0和1做key的时候,结果和EEP43中设计的结果不同,还是要看下一个版本号是怎么处理的,这个不小心就是个坑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Eshell V6.0  (abort with ^G)
1> M=#{1=>a}.   %% Construction syntax
#{1 => a}
2> M#{1.0 => b}.
#{1 => a,1.0 => b}
3> M#{1 => b}.
#{1 => b}
4> M#{1 := b}.
#{1 => b}
5> M#{1.0 := b}.
** exception error: bad argument
     in function  maps:update/3
        called as maps:update(1.0,b,#{1 => a})
     in call from erl_eval:'-expr/5-fun-0-'/2 (erl_eval.erl, line 249)
     in call from lists:foldl/3 (lists.erl, line 1261)
6> M2= #{1=>a,1=>b,1.0 =>c}.
#{1 => b,1.0 => c}
7> 1 == 1.0.
true
8> #{1.0 =>a ,1 =>b}.
#{1 => b,1.0 => a}

  

构造Map的时候我们重点要验证的就是"Maps in Erlang are ordered, Important!!!! – Maps with the same set of keys are always presented in the same way":

1
2
3
4
5
6
7
8
9
Eshell V6.0  (abort with ^G)
1> #{a=>124,b=>1024,c=>23}.
#{a => 124,b => 1024,c => 23}
2> #{b=>1024,c=>23,a=>124}.
#{a => 124,b => 1024,c => 23}
3> #{b=>1024,a=>124,c=>23}.
#{a => 124,b => 1024,c => 23}
4> M=#{b=>1024,a=>124,c=>23}.
#{a => 124,b => 1024,c => 23}

  

看下Map的基本操作,构造,更新,模式匹配.注意以下代码中 #{f:=F,a:={A,B}} =  M.做匹配的时候,前面的部分key是顺序无关的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Eshell V6.0  (abort with ^G)
1> M=#{a=>{1,2},b=>23,<<"OK">> =>ok, f=>fun()->receive a ->"Got a!" end end}.
#{a => {1,2},b => 23,f => #Fun<erl_eval.20.101568567>,<<"OK">> => ok}
2> M#{b=>1024}.
#{a => {1,2},b => 1024,f => #Fun<erl_eval.20.101568567>,<<"OK">> => ok}
3> M#{b2=>1023}.
#{a => {1,2},b => 23,b2 => 1023,f => #Fun<erl_eval.20.101568567>,<<"OK">> => ok}
4> M#{b2 :=1024}.
** exception error: bad argument
     in function  maps:update/3
        called as maps:update(b2,1024,
                              #{a => {1,2},b => 23,f => #Fun<erl_eval.20.101568567>,<<"OK">> => ok})
     in call from erl_eval:'-expr/5-fun-0-'/2 (erl_eval.erl, line 249)
     in call from lists:foldl/3 (lists.erl, line 1261)
5> M#{b :=1024}.
#{a => {1,2},b => 1024,f => #Fun<erl_eval.20.101568567>,<<"OK">> => ok}
6>  #{f:=F,a:={A,B}} =  M.
#{a => {1,2},b => 23,f => #Fun<erl_eval.20.101568567>,<<"OK">> => ok}
7> b().
A = 1
B = 2
F = fun() ->
           receive
               a ->
                   "Got a!"
           end
    end
M = #{a => {1,2},b => 23,f => #Fun<erl_eval.20.101568567>,<<"OK">> => ok}
ok
8>
 
  
 
  
 
2> M=#{a=>123,b=>234}.
#{a => 123,b => 234}
3> M#{a=>1000}.
#{a => 1000,b => 234}
4> M#{not_key=>1000}.
#{a => 123,b => 234,not_key => 1000}
5> M#{a :=1024}.
#{a => 1024,b => 234}
6> M#{not_key :=1024}.
** exception error: bad argument
     in function  maps:update/3
        called as maps:update(not_key,1024,#{a => 123,b => 234})
     in call from erl_eval:'-expr/5-fun-0-'/2 (erl_eval.erl, line 249)
     in call from lists:foldl/3 (lists.erl, line 1261)
7> P=#{name=>"zen",id=>2002}.
#{id => 2002,name => "zen"}
8> P#{naem=>1024}.
#{id => 2002,naem => 1024,name => "zen"}

  

上面更新字段名称的方式,是用=> 还是:= ?从project层面考虑,我会选择 := 运算,由于当给一个不存在的key进行赋值的时候,会抛出错误bad argument.而上面第7~8行代码,本意是更新name字段,可是由于拼写错误原来的字段值没有改动,仅仅添加了一错误字段.在project代码中显然:=会大大减少排错的成本.

然后就是经常使用的 Function Header Match,这里能够看到新增了两个Guard  erlang:is_map erlang:map_size

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
-module(a).
-compile(export_all).
test(#{state := {ok,State}} =S) ->
  State.
  
Eshell V6.0  (abort with ^G)
1> a:test().
** exception error: undefined function a:test/0
2> a:test(#{state => {ok,200}}).
200
3> a:test(#{state => {error,200}}).
** exception error: no function clause matching a:test(#{state => {error,200}}) (a.erl, line 7)
4>
  
 
  
 
 
-module(a).
-compile(export_all).
 
a(#{state := {ok,State}} = S) when erlang:is_map(S) ->
  State.
 
b(#{state := {ok,State}} = S) when erlang:map_size(S) >=1 ->
  State.
 
test(#{state := {ok,State}} =S) when erlang:is_map(S) andalso erlang:map_size(S) >=1 ->
  State.
 
Eshell V6.0  (abort with ^G)
1> a:b(#{state => {ok,200},id=>123,dd=>11}).
200
2> a:a(#{state => {ok,200},id=>123,dd=>11}).
200
3> a:test(#{state => {ok,200},id=>123,dd=>11}).
200
4>
4>

  

maps模块方面不知道变动还会有多少,就眼下提供的函数和EEP中的描写叙述还是有不少细微区别的,比方foldr foldl所有整合为fold函数;这个代码太长了,折叠一下:

  

比較!比較!
 
  Map的设计定位是data-type,那就存在Map与其他数据类型的比較规则,以及Map数据之间的比較规则.
 
[1] 与其他数据类型之间的比較
 
number < atom < reference < fun < port < pid < tuple <map < list < bit string
 
1
2
3
4
5
6
7
8
9
10
11> M= #{a=>123,b=>100}.
#{a => 123,b => 100}
12> M>12.
true
13> M> <<"12">>.
false
14> M> {a,b}.
true
15> M  > [1,2].
false

  

[2] Map之间比較
 
     Two different maps M1 and M2 are sorted first after size and secondly after their Key=>Value pairs.
      lists:sort([list_to_tuple(maps:to_list(M)) || M <- [M1,M2]). 
 

运算符优先级

没有应用在Map之间的运算符,仅仅有两个内部的运算符=> :=; => 用于创建和更新k-v, := 用于更新已经存在的k-v, :=在匹配过程用来从指定的key中提取Value值;

Why Maps?

我们已经有了record,dicts,gb_trees,ets,proplists,为什么还要Maps?Maps和现有的数据结构相比,最大的优势就是充分发挥Erlang模式匹配的威力. 我还关心的是之前的问题是否攻克了:

1.能够把record的name用作參数吗?

#RecordName{} 能够吗?

由于没有RecordName的限制了,所以这个问题自然消失;

2.能够把record的filed作为參数使用吗?

1
2
3
4
5
6
7
8
Eshell V6.0  (abort with ^G)
1> M=#{a=>{1,2},b=>23,<<"OK">> =>ok, f=>fun()->receive a ->"Got a!" end end}.
#{a => {1,2},b => 23,f => #Fun<erl_eval.20.101568567>,<<"OK">> => ok}
2> N=a.
a
3> #{N := Data} =M.
* 1: illegal use of variable 'N' in map
4>

  

而在代码模块中,以下的代码也会报错:illegal use of variable 'N' in map

test3(N,#{N := Data}=M)->
  Data.

3. a.b.c.d.e.f 能实现吗?

 
上次提到的a.b.c.d.e这样的fluent 式的写法吗?这是肯定被毙掉的方案.问题就出在点号上,一方面它是语句结束,一方面它还是浮点数中的小数点;以下就是一个很悲剧的样例:
 
1
2
3
4
5
6
1> M = #{ 1.1 => a, 1 => #{ 1 => b } }.
#{ 1 => #{ 1 => b }, 1.1 => a }.
 
2> #M.1.1.
a | b ?

  

4.record转proplists proplists转record

如今已经没有必要转换了

5.key仅仅能是atom

Maps中的Key能够是随意Erlang terms .

6.record往往要定义在hrl中

不须要了.

那就如今的情况,Maps会替代Record吗?

EEP43 的重要根据是Richard O'Keefes 的 No more need for records (fifth draft).能够说Maps缘起record替换方案.而Maps终于的设计目标是"Maps does not claim to be an replacement to records as the frames proposal does. Instead maps targets a larger usage domain and wishes to be a complement to records and supersede them where suitable."

从语言长远发展看,Map假设提供足够的便利,以及性能保障,淘汰掉record是一个开发人员主动选择的自然过程,是一个"Maps不杀record,record却因Maps而死"过程.对于开发人员倒不必有什么恐慌,record不会一夜之间消失,那么多的项目哪会在一朝一夕之间完毕过渡?顺其自然就好.

悬而未决的功能

有些语法特性在EEP43中提到了,可是在当前版本号(17.0-rc2)并没有提供;首当其冲的就是"Accessing a single value",要达到这个目的能够通过模式匹配完毕,也能够通过调用maps:get方法完毕,所以我对这种方法的期待度并不大.

1
2
3
4
5
6
7
8
9
10
11
Eshell V6.0  (abort with ^G)
1> M=#{a=>12,b=>200,c=>234}.
#{a => 12,b => 200,c => 234}
2> #{b := B}=M.
#{a => 12,b => 200,c => 234}
3> B.
200
4> #{b := B,c := C}=M.
#{a => 12,b => 200,c => 234}
5> #{b := B,c := C,d:=D}=M.
** exception error: no match of right hand side value #{a => 12,b => 200,c => 234}

 

其次就是Map comprehension ,我个人很喜欢list comprehension,所以对这个功能还是很期待的.

M1 = #{ E0 => E1 || K := V <- M0  }

OK,今天就到这里,期待Erlang新版本号的公布.

最后感谢支付宝付款的小伙伴们,昨天早晨老婆跟我说"短信通知有人在支付宝给你打钱了",我还说"这是什么新诈骗手段啊",验证之后真的是很惊喜,谢谢你们的认可和支持,我会继续努力的.

当我们谈论Erlang Maps时,我们谈论什么 Part 2的更多相关文章

  1. [Erlang 0117] 当我们谈论Erlang Maps时,我们谈论什么 Part 2

    声明:本文讨论的Erlang Maps是基于17.0-rc2,时间2014-3-4.后续Maps可能会出现语法或函数API上的有所调整,特此说明. 前情提要: [Erlang 0116] 当我们谈论E ...

  2. [Erlang 0116] 当我们谈论Erlang Maps时,我们谈论什么 Part 1

         Erlang 增加 Maps数据类型并不是很突然,因为这个提议已经进行了2~3年之久,只不过Joe Armstrong老爷子最近一篇文章Big changes to Erlang掀起不小了风 ...

  3. 当我们谈论Erlang Maps时,我们谈论什么 Part 1

         Erlang 添加 Maps数据类型并非非常突然,由于这个提议已经进行了2~3年之久,仅仅只是Joe Armstrong老爷子近期一篇文章Big changes to Erlang掀起不小了 ...

  4. [Erlang 0121] 当我们谈论Erlang Maps时,我们谈论什么 Part 3

    Erlang/OTP 17.0 has been released  http://www.erlang.org/download/otp_src_17.0.readme     Erlang/OTP ...

  5. 话题讨论&amp;征文--谈论大数据时我们在谈什么 获奖名单发布

    从社会发展趋势的角度,非常明显大数据会是眼下肉眼可及的视野范围里能看到的最大趋势之中的一个.从传统IT 业到互联网.互联网到移动互联网,从以智能手机和Pad 为主要终端载体的移动互联网到可穿戴设备的移 ...

  6. Erlang运行时的错误

    Erlang运行时发生错误时,会返回一些错误信息,理解这些信息,对于学好.用好Erlang来说是必要. Erlang中的运行错误包括:badarg, badarith, badmatch, funct ...

  7. 当我们看到phpinfo时在谈论什么

    我们在渗透测试的过程中,如果存在phpinfo界面,我们会想到什么? 大部分内容摘抄自:https://www.k0rz3n.com/2019/02/12/PHPINFO 中的重要信息/ 关于phpi ...

  8. 当我们在谈论 DevOps,我们在谈论什么?

    Cloud Insight 携手 BearyChat:打造适合运维人员的团队协作工具 走过 C 轮的 OneAPM,旗下的产品已经日渐丰满,从应用性能监控的 Application Insight 到 ...

  9. 项目 erlang启动时死循环

    机子里的otp是新装的 看了一下main 是在util:ensure_started一堆app的时候死讯了, 按照顺序是sasl crypto asn1 public_key ssl 发现是publi ...

随机推荐

  1. 关于Win 10的隐私保护政策

    近日.有人责备Win10收集用户信息,事实上这样的指责并不公平,比方:"Privacy Groups Claim Microsoft Uses Windows 10 as Big Broth ...

  2. Navicat Premium 12 模型导出sql

    找了半天,终于找到导出sql了!

  3. EOJ 3000 ROT13加密和解密

    应用 ROT13 到一段文字上仅仅只需要检查字母顺序并取代它在 13 位之后的对应字母,有需要超过时则重新绕回 26 英文字母开头即可.A 换成 N.B 换成 O.依此类推到 M 换成 Z,然后串行反 ...

  4. WebService中使用自定义类的解决方法(5种)

    转自:http://www.cnblogs.com/lxinxuan/archive/2007/05/24/758317.html Demo下载:http://files.cnblogs.com/lx ...

  5. Windows系统安装MySQL5.5.21图解教程

    大家都知道MySQL是一款中.小型关系型数据库管理系统,很具有实用性,对于我们学习很多技术都有帮助 数据库是5.5.21这个版本的.以下是安装步骤: 1.首先单击MySQL5.5.21的安装文件,出现 ...

  6. leetcode 系列文章目录

    leetcode 系列文章目录 0. 两数之和1. 两数相加 2. 无重复字符的最长子串 3. 寻找两个有序数组的中位数 4. 最长回文子串 5. Z 字形变换 6. 整数反转 7. 字符串转换整数 ...

  7. TCP/IP协议族简介

    OSI网络分层介绍 网络结构的标准模型是OSI模型,由国际互联网标准化组织定义的网络分层模型.虽然目前没有完全按照这种模型实现的网络协议栈,但是学习这个模型对于我们理解网络协议还是很有帮助的. 1.O ...

  8. JQuery学习系列篇(一)

    jQuery是一套Javascript脚本库:注意jQuery是脚本库, 而不是脚本框架. "库"不等于"框架", 比如"System程序集" ...

  9. Aspx小记

    关闭按钮 protected void Close_Click(object sender, EventArgs e) { //Page.RegisterStartupScript("clo ...

  10. PHP魔术方法__call()篇

    当我们调用类中的方法时,如果方法不存在的话.__call会是运行,从而使错误不显示出来 header('Content-type:text/html;charset="utf-8" ...