元组是用来保存一组数据元素的复合数据类型,其中数据元素是要求为erlang的数据类型,单不一定要是相同的类型,元组使用封闭的花括号{}来定义,里面的元素有逗号隔开。

  例如: 1> {1,2,3}.

      2> {1,{2,3},4}.

      3> {1,[2,3],4}.

      4> {1,"23",4}.

 

  tuple 的bifs函数:

  erlang:tuple_size(Tuple) ->Length. Tuple 的长度

  erlang:element(i,Tuple) -> aton(). 选择第i个位置的元素

  erlang:setelement(i,Tuple,Aton) ->Tuple2, 修改Tuple中的第i个位置的值为Aton。

 

  参考lists,得出一些类似lists的函数:

-module(com_record).

%% ====================================================================
%% API functions
%% ====================================================================
-export([map/2,
get_name/1,
foreach/2,
foldl/3,
foldl2/4,
foldl_index/4,
merge/3,
merge/4,
any/2,
any/3,
tuple_merge/3,
put_pd_fields/2,
get_pd_fields/2
]).

-spec any(Pred, T) -> boolean() when
Pred :: fun((Elem::term() ) -> boolean()),
T :: tuple().

%% @doc like lists:any/1
any(Pred, Tuple) ->
any(Pred, Tuple, 2).

any(_, {}, _) -> false;
any(Pred, Tuple, Begin) ->
S = tuple_size(Tuple),
if S < Begin ->
false;
true ->
any__(Pred, Tuple, Begin, S)
end.

any__(Pred, Tuple, M, M) ->
Pred(element(M, Tuple));
any__(Pred, Tuple, N, M) ->
case Pred(element(M, Tuple)) of
true ->
true;
_ ->
any__(Pred, Tuple, N+1, M)
end.

 


%% @doc record map
-spec map(Func, Record) -> Record2 when
Record :: Tuple,
Record2 :: Tuple,
Func :: fun((term()) -> term()),
Tuple :: tuple().
map(Func, Record) ->
[Name| List] = erlang:tuple_to_list(Record),
List2 = lists:map(Func, List),
erlang:list_to_tuple([Name | List2]).

-compile({inline, [get_name/1]}).
%% @doc get record name.
-spec get_name(tuple()) -> atom().
get_name(Record) ->
erlang:element(1, Record).


%% @doc foreach element not include name.
-spec foreach(Fun, Record) -> ok when
Fun :: fun((term()) -> _),
Record :: tuple().
foreach(Fun, Record) when is_tuple(Record) ->
com_util:for(2, erlang:tuple_size(Record), Fun).


%% @doc foldl element not include name.
-spec foldl(Fun, Acc0, Record) -> Acc1 when
Fun :: fun((term(), AccIn) -> AccOut),
Acc0 :: term(),
Acc1 :: term(),
AccIn :: term(),
AccOut :: term(),
Record :: tuple().

%% @doc not aceess record name.
foldl(Func, Acc, Record) when is_function(Func,2) ->
case erlang:tuple_size(Record) of
1 -> Acc;
M ->
foldl_(Func, Acc, Record, 2, M)
end.

foldl2(Func, Acc, Record, Begin)
when is_function(Func, 2) ->
case erlang:tuple_size(Record) of
S when S < Begin -> Acc;
S ->
foldl_(Func, Acc, Record, Begin, S)
end.


foldl_(Func, Acc, Record, N, N) ->
Func(erlang:element(N, Record), Acc);
foldl_(Func, Acc, Record, N, M) ->
foldl_(Func,
Func(erlang:element(N, Record), Acc),
Record, N+1, M).

%% @doc same foldl2 but fun take 3 arg
foldl_index(Func, Acc, Record, Begin) ->
case erlang:tuple_size(Record) of
S when S < Begin -> Acc;
S ->
foldl_index_(Func, Acc, Record, Begin, S)
end.

%% @doc merge two same type record with func
%% two record must same type.
%% Fun argument is record elements
%% @usage
%% merage(fun(A,B) -> A + B end, {aa, 1,1,1} , {aa, 3,3,3})
%% -> {aa, 4,4,4}
-spec merge(Fun, Record1, Record2) -> RecordOut when
Fun :: fun((A, B) -> T),
Record1 :: RecordOut,
Record2 :: RecordOut,
RecordOut :: tuple(),
A :: term(),
B :: term(),
T :: term().

merge(Fun, Record1, Record2) when is_function(Fun, 2) ->
merge__(Fun, Record1, Record2, 2, {get_name(Record1)}).

merge(Fun, Record1, Record2, Begin) when is_function(Fun, 2) ->
merge__(Fun, Record1, Record2, Begin, {get_name(Record1)}).

tuple_merge(Fun, T1, T2) ->
merge__(Fun, T1, T2, 1, {}).

merge__(Fun, T1, T2, Begin, Acc) ->
com_util:fold(Begin,
erlang:tuple_size(T1),
fun(Index, RecordOut) ->
erlang:append_element(RecordOut,
Fun(erlang:element(Index, T1),
erlang:element(Index, T2)))
end,
Acc
).

 

foldl_index_(Func, Acc, Record, N, N) ->
Func(N, erlang:element(N, Record), Acc);
foldl_index_(Func, Acc, Record, N, M) ->
foldl_index_(Func,
Func(N, erlang:element(N, Record), Acc),
Record, N+1, M).

 

%% HACK 最好的元转换
%% @doc 存入进程字典 每个filed 都会在前面加上pd_ 的前缀
%% 每个进程字典都只能是单次赋值
put_pd_fields(Record, FieldsName)
when erlang:is_list(FieldsName),
erlang:is_tuple(Record) ->
lists:foldl(fun(FName, Index) ->
case erlang:put(erlang:list_to_atom("pd_" ++ erlang:atom_to_list(FName)),
erlang:element(Index, Record))
of
undefined -> ok;
Ov -> io:format("ERR recrod_put ~p not first ~p\n", [FName, Ov])
end,
Index+1
end,
2,
FieldsName).

%% @doc Record
-spec get_pd_fields(RecordName, [atom()]) -> RecordOut when
RecordName :: atom(),
RecordOut :: tuple().

get_pd_fields(RecordName, FieldsName)
when erlang:is_list(FieldsName) ->
lists:foldl(fun(FName, R) ->
erlang:append_element(R,
erlang:get(erlang:list_to_atom("pd_" ++ erlang:atom_to_list(FName))))
end,
{RecordName},
FieldsName).


%%%%%% TEST unit

%-define(TEST, 1).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").

-record(tt, {id, name, age}).

pd_fields_test() ->
R= #tt{id=32, name= <<"feof">>, age=[1,2,4]},
put_pd_fields(R, record_info(fields, tt)),
?assertEqual(32, get(pd_id)),
?assertEqual(<<"feof">>, get(pd_name)),
?assertEqual([1,2,4], get(pd_age)),

R2 = get_pd_fields(tt, record_info(fields, tt)),
?assertEqual(R2, R),

ok.

-endif.