• 当我们谈论Erlang Maps时,我们谈论什么 Part 2


     

      声明:本文讨论的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新版本号的公布.

     

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

  • 相关阅读:
    微信小程序,搜索结果关键词高亮 wxml不能动态识别html标签
    关于vue 全局loading方案
    element ui树样式问题
    Promise.all( ) 的使用
    关于form表单校验问题
    vue 异步改同步 获取结果 动态函数
    1.关于数据for循环不要用index作为key,2.面二次刷新404问题(空白) 的探讨 3. vue图片上传
    element ui点击切换皮肤
    关于element ui input、以及button样式不能覆盖的解决办法(登录页面)
    vue版本根据当前路由匹配到根父节点并且激活
  • 原文地址:https://www.cnblogs.com/jzssuanfa/p/7091088.html
Copyright © 2020-2023  润新知