proplists.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 635 行 · 第 1/2 页

ERL
635
字号
%% ---------------------------------------------------------------------%% @spec delete(Key::term(), List::[term()]) -> [term()]%%%%%% @doc Deletes all entries associated with <code>Key</code> from%% <code>List</code>.delete(Key, [P | Ps]) ->    if is_atom(P), P =:= Key ->	    delete(Key, Ps);       is_tuple(P), size(P) >= 1, element(1, P) =:= Key ->	    delete(Key, Ps);       true ->	    [P | delete(Key, Ps)]    end;delete(_, []) ->    [].%% ---------------------------------------------------------------------%% @spec substitute_aliases(Aliases, List::[term()]) -> [term()]%%%%	    Aliases = [{Key, Key}]%%	    Key = term()%%%% @doc Substitutes keys of properties. For each entry in%% <code>List</code>, if it is associated with some key <code>K1</code>%% such that <code>{K1, K2}</code> occurs in <code>Aliases</code>, the%% key of the entry is changed to <code>Key2</code>. If the same%% <code>K1</code> occurs more than once in <code>Aliases</code>, only%% the first occurrence is used.%%%% <p>Example: <code>substitute_aliases([{color, colour}], L)</code>%% will replace all tuples <code>{color, ...}</code> in <code>L</code>%% with <code>{colour, ...}</code>, and all atoms <code>color</code>%% with <code>colour</code>.</p>%%%% @see substitute_negations/2%% @see normalize/2substitute_aliases(As, Props) ->    [substitute_aliases_1(As, P) || P <- Props].substitute_aliases_1([{Key, Key1} | As], P) ->    if is_atom(P), P =:= Key ->	    property(Key1, true);       is_tuple(P), size(P) >= 1, element(1, P) =:= Key ->	    property(setelement(1, P, Key1));       true ->	    substitute_aliases_1(As, P)    end;substitute_aliases_1([], P) ->    P.%% ---------------------------------------------------------------------%% @spec substitute_negations(Negations, List::[term()]) -> [term()]%%%%	    Negations = [{Key, Key}]%%	    Key = term()%%%% @doc Substitutes keys of boolean-valued properties and simultaneously%% negates their values. For each entry in <code>List</code>, if it is%% associated with some key <code>K1</code> such that <code>{K1,%% K2}</code> occurs in <code>Negations</code>, then if the entry was%% <code>{K1, true}</code> it will be replaced with <code>{K2,%% false}</code>, otherwise it will be replaced with <code>{K2,%% true}</code>, thus changing the name of the option and simultaneously%% negating the value given by <code>get_bool(List)</code>. If the same%% <code>K1</code> occurs more than once in <code>Negations</code>, only%% the first occurrence is used.%%%% <p>Example: <code>substitute_negations([{no_foo, foo}], L)</code>%% will replace any atom <code>no_foo</code> or tuple <code>{no_foo,%% true}</code> in <code>L</code> with <code>{foo, false}</code>, and%% any other tuple <code>{no_foo, ...}</code> with <code>{foo,%% true}</code>.</p>%%%% @see get_bool/2%% @see substitute_aliases/2%% @see normalize/2substitute_negations(As, Props) ->    [substitute_negations_1(As, P) || P <- Props].substitute_negations_1([{Key, Key1} | As], P) ->    if is_atom(P), P =:= Key ->	    property(Key1, false);       is_tuple(P), size(P) >= 1, element(1, P) =:= Key ->	    case P of		{_, true} ->		    property(Key1, false);		{_, false} ->		    property(Key1, true);		_ ->		    %% The property is supposed to be a boolean, so any		    %% other tuple is interpreted as `false', as done in		    %% `get_bool'.		    property(Key1, true)	    end;		           true ->	    substitute_negations_1(As, P)    end;substitute_negations_1([], P) ->    P.%% ---------------------------------------------------------------------%% @spec expand(Expansions, List::[term()]) -> [term()]%%%%	    Expansions = [{property(), [term()]}]%%%% @doc Expands particular properties to corresponding sets of%% properties (or other terms). For each pair <code>{Property,%% Expansion}</code> in <code>Expansions</code>, if <code>E</code> is%% the first entry in <code>List</code> with the same key as%% <code>Property</code>, and <code>E</code> and <code>Property</code>%% have equivalent normal forms, then <code>E</code> is replaced with%% the terms in <code>Expansion</code>, and any following entries with%% the same key are deleted from <code>List</code>.%%%% <p>For example, the following expressions all return <code>[fie, bar,%% baz, fum]</code>:%% <ul>%%   <li><code>expand([{foo, [bar, baz]}],%%                    [fie, foo, fum])</code></li>%%   <li><code>expand([{{foo, true}, [bar, baz]}],%%                    [fie, foo, fum])</code></li>%%   <li><code>expand([{{foo, false}, [bar, baz]}],%%                    [fie, {foo, false}, fum])</code></li>%% </ul>%% However, no expansion is done in the following call:%% <ul>%%   <li><code>expand([{{foo, true}, [bar, baz]}],%%                    [{foo, false}, fie, foo, fum])</code></li>%% </ul>%% because <code>{foo, false}</code> shadows <code>foo</code>.</p>%%%% <p>Note that if the original property term is to be preserved in the%% result when expanded, it must be included in the expansion list. The%% inserted terms are not expanded recursively. If%% <code>Expansions</code> contains more than one property with the same%% key, only the first occurrance is used.</p>%%%% @see normalize/2expand(Es, Ps) when is_list(Ps) ->    Es1 = [{property(P), V} || {P, V} <- Es],    flatten(expand_0(key_uniq(Es1), Ps)).%% Here, all key properties are normalized and there are no multiple%% entries in the list of expansions for any specific key property. We%% insert the expansions one at a time - this is quadratic, but gives%% the desired behaviour in a simple way.expand_0([{P, L} | Es], Ps) ->    expand_0(Es, expand_1(P, L, Ps));expand_0([], Ps) ->    Ps.expand_1(P, L, Ps) ->    %% First, we must find out what key to look for.    %% P has a minimal representation here.    if is_atom(P) ->	    expand_2(P, P, L, Ps);       is_tuple(P), size(P) >= 1 ->	    expand_2(element(1, P), P, L, Ps);       true ->	    Ps    % refuse to expand non-property    end.expand_2(Key, P1, L, [P | Ps]) ->    if is_atom(P), P =:= Key ->	    expand_3(Key, P1, P, L, Ps);       is_tuple(P), size(P) >= 1, element(1, P) =:= Key ->	    expand_3(Key, P1, property(P), L, Ps);       true ->	    %% This case handles non-property entries, and thus	    %% any already inserted expansions (lists), by simply	    %% ignoring them.	    [P | expand_2(Key, P1, L, Ps)]    end;expand_2(_, _, _, []) ->    [].expand_3(Key, P1, P, L, Ps) ->    %% Here, we have found the first entry with a matching key. Both P    %% and P1 have minimal representations here. The inserted list will    %% be flattened afterwards. If the expansion is done, we drop the    %% found entry and alao delete any later entries with the same key.    if P1 =:= P ->	    [L | delete(Key, Ps)];       true ->	    %% The existing entry does not match - keep it.	    [P | Ps]    end.key_uniq([{K, V} | Ps]) ->    [{K, V} | key_uniq_1(K, Ps)];key_uniq([]) ->    [].key_uniq_1(K, [{K1, V} | Ps]) ->    if K =:= K1 ->	    key_uniq_1(K, Ps);       true ->	    [{K1, V} | key_uniq_1(K1, Ps)]    end;key_uniq_1(_, []) ->    [].%% This does top-level flattening only.flatten([E | Es]) when is_list(E) ->    E ++ flatten(Es);flatten([E | Es]) ->    [E | flatten(Es)];flatten([]) ->    [].%% ---------------------------------------------------------------------%% @spec normalize(List::[term()], Stages::[Operation]) -> [term()]%%%%	    Operation = {aliases, Aliases} | {negations, Negations}%%                    | {expand, Expansions}%%	    Aliases = [{Key, Key}]%%	    Negations = [{Key, Key}]%%	    Key = term()%%	    Expansions = [{property(), [term()]}]%%%% @doc Passes <code>List</code> through a sequence of%% substitution/expansion stages. For an <code>aliases</code> operation,%% the function <code>substitute_aliases/2</code> is applied using the%% given list of aliases; for a <code>negations</code> operation,%% <code>substitute_negations/2</code> is applied using the given%% negation list; for an <code>expand</code> operation, the function%% <code>expand/2</code> is applied using the given list of expansions.%% The final result is automatically compacted (cf.%% <code>compact/1</code>).%%%% <p>Typically you want to substitute negations first, then aliases,%% then perform one or more expansions (sometimes you want to pre-expand%% particular entries before doing the main expansion). You might want%% to substitute negations and/or aliases repeatedly, to allow such%% forms in the right-hand side of aliases and expansion lists.</p>%%%% @see substitute_aliases/2%% @see substitute_negations/2%% @see expand/2%% @see compact/1normalize(L, [{aliases, As} | Xs]) ->    normalize(substitute_aliases(As, L), Xs);normalize(L, [{expand, Es} | Xs]) ->    normalize(expand(Es, L), Xs);normalize(L, [{negations, Ns} | Xs]) ->    normalize(substitute_negations(Ns, L), Xs);normalize(L, []) ->    compact(L).%% ---------------------------------------------------------------------%% @spec split(List::[term()], Keys::[term()]) -> {Lists, Rest}%%           Lists = [[term()]]%%           Rest = [term()]%%%% @doc Partitions <code>List</code> into a list of sublists and a%% remainder. <code>Lists</code> contains one sublist for each key in%% <code>Keys</code>, in the corresponding order. The relative order of%% the elements in each sublist is preserved from the original%% <code>List</code>. <code>Rest</code> contains the elements in%% <code>List</code> that are not associated with any of the given keys,%% also with their original relative order preserved.%%%% <p>Example:<pre>%% split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])</pre>%% returns<pre>%% {[[a], [{b, 5}, b],[{c, 2}, {c, 3, 4}]], [{e, 1}, d]}</pre>%% </p>split(List, Keys) ->    {Store, Rest} = split(List, dict:from_list([{K, []} || K <- Keys]), []),    {[lists:reverse(dict:fetch(K, Store)) || K <- Keys],     lists:reverse(Rest)}.split([P | Ps], Store, Rest) ->    if is_atom(P) ->	    case dict:is_key(P, Store) of		true ->		    split(Ps, dict_prepend(P, P, Store), Rest);		false ->		    split(Ps, Store, [P | Rest])	    end;       is_tuple(P), size(P) >= 1 ->	    %% Note that Key does not have to be an atom in this case.	    Key = element(1, P),	    case dict:is_key(Key, Store) of		true ->		    split(Ps, dict_prepend(Key, P, Store), Rest);		false ->		    split(Ps, Store, [P | Rest])	    end;       true ->	    split(Ps, Store, [P | Rest])    end;split([], Store, Rest) ->    {Store, Rest}.dict_prepend(Key, Val, Dict) ->    dict:store(Key, [Val | dict:fetch(Key, Dict)], Dict).

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?