• Erlang--proplists结构解析


    proplists 模块适用数据量较少的场景,处理配置文件和函数选项时常用.proplists对内部数据结构是Key-Value键值对形式,第一个元素做key用来查询和删除,如果一个key有多个值就会使用第一次出现的值,其它被忽略.proplists对于Key和Value的约束极其宽松,可以是任意term().甚至可以把{Atom,true}缩写成为Atom.也正是由于这样宽松的数据约束,proplists模块没有更新和追加数据项的方法,需要使用lists:replace/4.Key进行比较使用的是=:=精确等于,会判断类型和值.
     
     
    5> proplists:get_value(1,[{1,a},{1.0,b},{1,c}]).
    a
    6> proplists:append_values(1,[{1,a},{1.0,b},{1,c}]).
    [a,c]
    8>

    规范与压缩

      上面提到Atom缩写的形式,atom的形式成为压缩格式,{Atom,true}的形式成为规范形式.这是Property的两种形式,其定义是 -type property()   :: atom() | tuple().
    模块里面有一个property方法专门进行数据规范化.
    复制代码
    property({Key, true}) when is_atom(Key) ->
        Key;
    property(Property) ->
        Property.
    调用的形式:
    16>  proplists:property({a}).
    {a}
    17>  proplists:property({a,true}).
    a
    复制代码
    压缩形式其实就是逐一对proplists内的元素执行property/1,即:[property(P) || P <- List].
    10>  proplists:compact( [{a, true}, {b, true}, {a, 3}, {c, true}, {a, [4]}]).
    [a,b,{a,3},c,{a,[4]}]
    计算有压缩就会有展开的函数
    复制代码
    unfold([P | Ps]) ->
        if is_atom(P) ->
         [{P, true} | unfold(Ps)];
           true ->
         [P | unfold(Ps)]
        end;
    unfold([]) ->
        [].
    
    12>  proplists:unfold([foo,bar,test,haha]).
    [{foo,true},{bar,true},{test,true},{haha,true}]
    13>  proplists:unfold([foo,bar,{test,false},haha]).
    [{foo,true},{bar,true},{test,false},{haha,true}]
    14>  proplists:unfold([foo,"zen",bar,{test,false},haha]).
    [{foo,true},"zen",{bar,true},{test,false},{haha,true}]
    15>  proplists:unfold([foo,"zen",23,{test,false},haha]).
    [{foo,true},"zen",23,{test,false},{haha,true}]
    复制代码
     

    proplists 相关操作

    下面看一下proplists的常规操作,有一些方法还是要注意一下细节的.

     append_values (注意上图中少拼写了一个s )将所有key值相同的数据项,value整合在list中.
    复制代码
    1> proplists:append_values(a, [{a, [1,2]}, {b, 0}, {a, 3}, {c, -1}, {a, [4]}]).
    [1,2,3,4]
    2> proplists:append_values(a, [{a, b}, {b, 0}, {a, 3}, {c, -1}, {a, [4]}]).   
    [b,3,4]
    3> proplists:append_values(a, [{a, b}, {b, 0}, {a, 3}, {c, -1}, {a, [[4]]}]).
    [b,3,[4]]
    
    
    1> proplists:append_values(a, [{a, [1,2]}, {"zen", 0}, {a, 3}, {c, -1}, {a, [4]}
    ]).
    [1,2,3,4]
    2> proplists:append_values(a, [{a, [1,2]},b]).
    [1,2]
    3> proplists:append_values(b, [{a, [1,2]},b]).
    [true]
    4>
    复制代码
     
    delete方法会删除所有等于Key值的数据项:
    %% delete(Key, List) -> List
    18>  proplists:delete(a, [{a, true}, {b, true}, {a, 3}, {c, true}, {a, [4]}]).
    [{b,true},{c,true}]
     
    compact将数据进行压缩
    10>  proplists:compact( [{a, true}, {b, true}, {a, 3}, {c, true}, {a, [4]}]).
    [a,b,{a,3},c,{a,[4]}]

     

    get_all_values 获取所有等于Key值的数据项:

    8> proplists:get_all_values(a, [{a, [1,2]}, {"zen", 0}, {a, 3}, {c, -1}, {a, [4]
    }]).
    [[1,2],3,[4]]
    9>
    get_bool 这个方法还是有点陷阱的,其意图是看Key值第一次出现时的值是true|false. 
    复制代码
    get_bool(Key, [P | Ps]) ->
        if is_atom(P), P =:= Key ->  true;
                   
           tuple_size(P) >= 1, element(1, P) =:= Key ->
                           case P of
                                {_, true} ->
                                    true;
                                _ ->
                                    %% Don't continue the search!
                                    false
                           end;
           true ->
                  get_bool(Key, Ps)
        end;
    get_bool(_Key, []) ->
        false.
    
    9> proplists:get_bool(a, [{a, [1,2]}, {"zen", 0}, {a, 3}, {c, -1}, {a, [4]}]).
    false
    10> proplists:get_bool(a, [{a, [1,2]}, {"zen", 0}, {a, 3},a, {c, -1}, {a, [4]}]).
    false
    11> proplists:get_bool(a, [a,{a, [1,2]}, {"zen", 0}, {a, 3},a, {c, -1}, {a, [4]}]).
    true
    12> proplists:get_bool(a, [{a,true},{a, [1,2]}, {"zen", 0}, {a, 3},a, {c, -1}, {a, [4]}]).
    true
    13> proplists:get_bool(a, [{a,false},{a, [1,2]}, {"zen", 0}, {a, 3},a, {c, -1},{a, [4]}]).
    false
    14> proplists:get_bool(q, [{a,true},{a, [1,2]}, {"zen", 0}, {a, 3},a, {c, -1}, {a, [4]}]).
    false
    15> proplists:get_bool(q, ["abc",{a,true},{a, [1,2]}, {"zen", 0}, {a, 3},a, {c,-1}, {a, [4]}]).
    false
    16> proplists:get_bool("abc", ["abc",{a,true},{a, [1,2]}, {"zen", 0}, {a, 3},a,{c, -1}, {a, [4]}]).
    false
    17> proplists:get_bool("abc", [{"abc",true},{a,true},{a, [1,2]}, {"zen", 0}, {a,
    3},a, {c, -1}, {a, [4]}]).
    true
    复制代码
     get_keys 获取所有不重复的keys
    18> proplists:get_keys([{"abc",true},{a,true},{a, [1,2]}, {"zen", 0}, {a, 3},a,{c, -1}, {a, [4]}]).
    ["zen",a,c,"abc"]
    19> proplists:get_keys([{a,true},{a,true},{a, [1,2]}, {"zen", 0}, {a, 3},a, {c,-1}, {a, [4]}]).
    ["zen",a,c]
    get_value 按Key取值,取得是第一次出现的Value
    复制代码
    get_value(Key, [P | Ps], Default) ->
        if is_atom(P), P =:= Key ->
                    true;
           tuple_size(P) >= 1, element(1, P) =:= Key ->
                     case P of
                              {_, Value} ->
                                  Value;
                              _ ->
                                  %% Don</code>t continue the search!
                                  Default
                     end;
           true ->
             get_value(Key, Ps, Default)
        end;
    get_value(_Key, [], Default) ->
        Default.
    
    3> proplists:get_value([a,b], ["packet",[a,b],"login",22,2,s,f] , "none").
    "none"
    4> proplists:get_value("login", ["packet",[a,b],"login",22,2,s,f] , "none").
    "none"
    5> proplists:get_value(login, ["packet",[a,b],"login",22,2,s,f] , "none").  
    "none" 
    
    1>  proplists:get_value([a,b], ["packet",{[a,b],bingo},"login",22,2,s,f] , "none").
    bingo
    2>  proplists:get_value(s, ["packet",{[a,b],bingo},"login",22,2,s,f] , "none").
    true
    3>
    复制代码
    look_up 与get_value不同的是这里返回的是{Key,Value}
    6> proplists:lookup(a, [{a, b}, {b, 0}, {a, 3}, {c, -1}, {a, [[4]]}]). 
    {a,b}
    7> proplists:lookup(a, [{a,1},{a, b}, {b, 0}, {a, 3}, {c, -1}, {a, [[4]]}]).
    {a,1}
    8> 
    lookup_all 
    8> proplists:lookup_all(a, [{a,1},{a, b}, {b, 0}, {a, 3}, {c, -1}, {a, [[4]]}]).
    [{a,1},{a,b},{a,3},{a,[[4]]}]
     
    is_defined 是否存在特定Key值
    6>  proplists:is_defined(s, ["packet",{s,kill},{[a,b],bingo},"login",22,2,s,f]).
    true
    7>  proplists:is_defined(p, ["packet",{s,kill},{[a,b],bingo},"login",22,2,s,f]).
    false
    8>
    split 按照Key值进行数据分组
    9> proplists:split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c]).
    {[[a],[{b,5},b],[{c,2},{c,3,4}]],[{e,1},d]}
    10> proplists:split([{c, 2}, {c,23},{a,false},{e, 1}, a, {c, 3, 4}, d, {b, 5}, b
    ], [a, b, c]).
    {[[{a,false},a],[{b,5},b],[{c,2},{c,23},{c,3,4}]],[{e,1},d]}
    11>

    单独一组

    下面这几个方法我们放在一起看
     
     expand 做的是把list中的Key替换成对应Value ,注意, 这个方法展开的对象是Property
    复制代码
    8> proplists: expand([{foo, [bar, baz]}],[fie, foo, fum]).
    [fie,bar,baz,fum]
    9> proplists: expand([{foo, [bar, baz]},{fie,ok},{fum,100}],[fie, foo, fum]).
    [ok,bar,baz,100]
    10> proplists: expand([{foo, [bar, baz]},{fie,[[ok]]},{fum,"100"}],[fie, foo, fum]).
    [[ok],bar,baz,49,48,48]
    
    12> proplists: expand([{"fie",23},{1,{1}},{1.0,{29}},{foo, [bar, baz]},{fie,[[ok]]},{fum,"100"}],["fie",1, foo, fum]).
    [102,105,101,1,bar,baz,49,48,48]
    13> ${.
    123
    14> [102,105,101].
    "fie"
    15> 
    复制代码
     substitute_aliases 将对应的key值替换为别名
    1> proplists:substitute_aliases([{zen,"ligaoren"},{0,zero}],[zen,{zen,zen},{abc,zen},{zen,tick},0,{0,1},{23,0}]).
    [{"ligaoren",true},{"ligaoren",zen},{abc,zen},{"ligaoren",tick},0,{zero,1},{23,0}]
    2>
    substitute_negations key值替换,value取反
    复制代码
    2> proplists:substitute_negations([{zen,"ligaoren"},{0,zero}],[zen,{zen,zen},{abc,zen},{zen,tick},0,{0,1},{23,0}]).
    [{"ligaoren",false},{"ligaoren",true},{abc,zen},{"ligaoren",true},0,zero,{23,0}]
    
    3> proplists:substitute_negations([{zen,"ligaoren"},{0,zero}],[zen,{zen,zen},{abc,zen},{zen,tick},0,{0,true},{23,0}]).
    [{"ligaoren",false},{"ligaoren",true},{abc,zen},{"ligaoren",true},0,{zero,false},{23,0}]
    
    4> proplists:substitute_negations([{zen,"ligaoren"},{0,zero}],[zen,{zen,zen},{abc,zen},{zen,tick},0,{0,false},{23,0}]).
    [{"ligaoren",false},{"ligaoren",true},{abc,zen},{"ligaoren",true},0,zero,{23,0}]
    复制代码
     normalize 整合了substitute_aliases   substitute_negations  expand
    复制代码
    2> proplists:normalize( [a,b,c,d,e,f,g],[{aliases, [{b,b2},{e,email}]} ]).
    [a,b2,c,d,email,f,g]
    3> proplists:normalize([a,b,c,d,e,f,g],[{aliases, [{b,b2},{e,email}]} ]).                                                                
    [a,b2,c,d,email,f,g]
    4> proplists:normalize([a,b,c,d,e,f,g],[{aliases, [ {negations, [a,f]}]} ]).
    [a,b,c,d,e,f,g]
    5> proplists:normalize([a,b,c,d,e,f,g],[ {expand, [{d,do_it_by_yourself},{g,1000}]}]).                                                   
    [a,b,c,do_it_by_yourself,e,f,1000]
    复制代码

     应用举例

    复制代码
    6> test:module_info(compile).         
    [{options,[{outdir,"/zen/temp"}]},
    {version,"4.8"},
    {time,{2012,6,15,2,3,23}},
    {source,"/zen/temp/test.erl"}] 
    7> proplists:get_value(time,test:module_info(compile)).
    {2012,6,15,2,3,23}
    8> 
    复制代码
     
    mochiweb项目解析Header Cookie多处使用了proplist: 
    复制代码
    parse_form_outer(eof, _, Acc) ->
        lists:reverse(Acc);
    parse_form_outer({headers, H}, FileHandler, State) ->
        {"form-data", H1} = proplists:get_value("content-disposition", H),
        Name = proplists:get_value("name", H1),
        Filename = proplists:get_value("filename", H1),
        case Filename of
            undefined ->
                fun (Next) ->
                        parse_form_value(Next, {Name, []}, FileHandler, State)
                end;
            _ ->
                ContentType = proplists:get_value("content-type", H),
                Handler = FileHandler(Filename, ContentType),
                fun (Next) ->
                        parse_form_file(Next, {Name, Handler}, FileHandler, State)
                end
        end.
    复制代码

    解析选项一例:

    复制代码
    %%% 18> proplists:lookup(loop,[{ip, "127.0.0.1"},{loop, {mochiweb_http, default_body}}]).
    %%%   {loop,{mochiweb_http,default_body}}
    
    parse_options(Options) ->
        {loop, HttpLoop} = proplists:lookup(loop, Options),
        Loop = fun (S) ->
                       ?MODULE:loop(S, HttpLoop)
               end,
        Options1 = [{loop, Loop} | proplists:delete(loop, Options)],
        mochilists:set_defaults(?DEFAULTS, Options1).
    复制代码

    最后:估计90%的情况下,我们只使用proplists:get_value   : )

    2012-8-22更新

       proplists:get_value的性能要比lists:keyfind差很多,

       lists的下面几个方法都是BIF实现:%% Bifs: keymember/3, keysearch/3, keyfind/3

       而proplists:get_value是Erlang实现,我觉得这是产生性能差异的根本原因;

     下面有一个相关讨论基本上是同样的判断: http://www.ostinelli.net/erlang-listskeyfind-or-proplistsget_value/

    复制代码
    -module(pvsl).
    -define(LIST_SIZES, [10000, 100000, 1000000]).
    -define(RETRIES, 1000).
    -compile(export_all).
    
    start() ->
            % test for different list sizes
            lists:foreach(fun(N) -> test_list(N) end, ?LIST_SIZES).
    
    test_list(ListSize) ->
            % generate a list of size ListSize of {Key, Val} entries
            KeyList = [{K, K} || K <- lists:seq(1, ListSize)],
            % test this list against both functions
            lists:foreach(fun(Type) -> get_val(Type, now(), KeyList, ListSize, ?RETRIES) end,
                    [proplists, lists]).
    
    % test getting values, compute necessary time and output print results
    get_val(Type, Start, _KeyList, ListSize, 0) ->
            T = timer:now_diff(now(), Start),
            io:format("computed ~p random key searches on a ~p-sized list in ~p ms using ~p~n",
                    [?RETRIES, ListSize, T/1000, Type]);
    get_val(proplists, Start, KeyList, ListSize, Tries) ->
            proplists:get_value(random:uniform(ListSize), KeyList),
            get_val(proplists, Start, KeyList, ListSize, Tries - 1);
    get_val(lists, Start, KeyList, ListSize, Tries) ->
            lists:keyfind(random:uniform(ListSize), 1, KeyList),
            get_val(lists, Start, KeyList, ListSize, Tries - 1).
    
    I ran this test on my MacBook Pro, Intel Core i5 2.4GHz with 4GB Memory, and Erlang R13B04, with Kernel Polling enabled. These are the results.
    roberto$ erl +K true +P 1000000
    Erlang R13B04 (erts-5.7.5) [source] [smp:4:4] [rq:4] [async-threads:0] [hipe] [kernel-poll:true]
    
    Eshell V5.7.5  (abort with ^G)
    1> c(pvsl).
    {ok,pvsl}
    2> pvsl:start().
    computed 1000 random key searches on a 10000-sized list in 323.373 ms using proplists
    computed 1000 random key searches on a 10000-sized list in 12.897 ms using lists
    computed 1000 random key searches on a 100000-sized list in 3273.973 ms using proplists
    computed 1000 random key searches on a 100000-sized list in 130.592 ms using lists
    computed 1000 random key searches on a 1000000-sized list in 34131.905 ms using proplists
    computed 1000 random key searches on a 1000000-sized list in 2050.627 ms using lists
    ok
    3>
    复制代码
  • 相关阅读:
    庆祝 杀马下载量突破300万!
    智能实验室-杀马(Defendio) 4.26.0.940
    智能实验室-全能优化(Guardio) 4.992.0.900
    智能实验室-杀马(Defendio) 4.17.0.850
    目前为止功能最全的基于silverlight4(beta)的摄像头应用
    Discuz!NT负载均衡方案
    Discuz!NT跨站缓存同步
    Discuz!NT负载均衡解决方案(HA)之LVS(Linux Virtual Server)
    Discuz!NT企业版之Sphinx全文搜索(下)
    Discuz!NT千万级数据量上的两驾马车TokyoCabinet,MongoDB
  • 原文地址:https://www.cnblogs.com/likui360/p/6185257.html
Copyright © 2020-2023  润新知