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>

时间: 2024-10-17 20:04:36

Erlang--proplists结构解析的相关文章

RTP协议之Header结构解析

实时传输协议 RTP,RTP 提供带有实时特性的端对端数据传输服务,传输的数据如:交互式的音频和视频.那些服务包括有效载荷类型定义,序列号,时间戳和传输监测控制.应用程序在 UDP 上运行 RTP 来使用它的多路技术和 checksum 服务.2 种协议都提供传输协议的部分功能.不过,RTP 可能被其他适当的下层网络和传输协议使用.如 果下层网络支持,RTP 支持数据使用多播分发机制转发到多个目的地. 注意 RTP 本身没有提供任何的机制来确保实时的传输或其他的服务质量保证,而是由低层的服务来完

H.264码流结构解析

大概前五六年之前写过的一个大体分析H.264格式,不是很详细,可以大致看看有哪些格式. H.264码流结构解析 那个时候上传的百度文库,以前记得有多积分,现在都不能下载了,还要充钱才可以.真是~~~ 1. H.264简介 MPEG(Moving Picture Experts Group)和VCEG(Video Coding Experts Group)已经联合开发了一个比早期研发的MPEG 和H.263性能更好的视频压缩编码标准,这就是被命名为AVC(Advanced Video Coding

android sdk api结构解析

一.系统级:android.accounts android.app     1.OS 相关         android.os         android.os.storage         android.hardware(传感器)         android.security         android.drm(这个应该是为所有app服务的)     2.VM 相关         dalvik.system         dalvik.bytecode 二.程序框架  

erlang实现DNS解析域名

erlang的DNS解析,有个inet模块,可以做这个事情,对应的api如下:(这里用的R16B的版本) inet:getaddr/2解析对应一个ip,而inet:getaddr/2解析有可能对应1个或多个映射的IP. 第一个参数为host域名,第二个参数是ipv4或ipv6. 如下图: 简单写了下测试代码,直接上代码: enter() -> [ resolve(Host) || Host <- domains()]. %%域名列表 domains() -> ["www.bai

网易视频云:HBase – 存储文件HFile结构解析

网易视频云是网易推出的PAAS视频云服务,主要应用于在线教育.直播秀场.远程医疗.企业协作等领域.今天,网易视频云技术专家与大家分享一下:HBase – 存储文件HFile结构解析. HFile是HBase存储数据的文件组织形式,参考BigTable的SSTable和Hadoop的TFile实现.从HBase开始到现在,HFile经历了三个版本,其中V2在0.92引入,V3在0.98引入.HFileV1版本的在实际使用过程中发现它占用内存多,HFile V2版本针对此进行了优化,HFile V3

MBR结构解析与fdisk的bash实现

一.MBR结构解析 首先我们先介绍一些MBR的基本知识基础,再晾图片分析. MBR主要分为三大块各自是: 1.载入引导程序(446K) 2.分区表(64k) 3.标志结束位(2k) 载入引导程序:内容是因机器而异它里面正如其名.就是存放载入引导程序.如今基本的载入引导程序是LILO(LInux LOader)和 GNU GRUB(GRand Unified Boot loader). 分区表:里面主要记录4个16K主分区的信息.我们将在下文进行具体介绍. 表示结束位:就是标志MBR结束.通常是0

Linux内核 hlist_head/hlist_node结构解析

linux内核里面的双向循环链表和哈希链表有什么不同呢?1.双向循环链表是循环的,哈希链表不是循环的 2.双向循环链表不区分头结点和数据结点,都用list_head表示,而哈希链表区分头结点(hlist_head)和数据结点(hlist_node).与哈希链表有关的两个数据结构如下: struct hlist_head { struct hlist_node *first; //指向每一个hash桶的第一个结点的指针 }; struct hlist_node { struct hlist_nod

Erlang--etc结构解析

Erlang中可以用List表达集合数据,但是如果数据量特别大的话在List中访问元素就会变慢了;这种主要是由于List的绝大部分操作都是基于遍历完成的. Erlang的设计目标是软实时(参考:http://en.wikipedia.org/wiki/Real-time_computing),在大量数据中检索的时间不仅要快而且要求是常量.为了解决快速查 询的问题,Erlang提供的机制就是ETS(Erlang Term Storage)和DETS(Disk Erlang Term Storage

jQuery学习之结构解析

jQuery内核解析 1.jQuery整体的结构是一个匿名函数 (function( window, undefined ) {})(window); 2.jQuery就是一个很普通的函数,也是一个很普通的对象,还是一个构造器函数 var jQuery = function( selector, context ) { return new jQuery.fn.init( selector, context ); } 3.window.jQuery相当于把jQuery作为window的一个属性,