Erlang Thursday - ETS,匹配规则和函数

上星期的Erlang Thursday我展示了怎样通过使用ets:select带来的好处而且使我们的查询更丰富的优势来结束文章。

首先我们将需要一个新的ETS表,那么我们以一个新的public类型的“产品”表开始,并且按我们的标准来创建一个进程然后将表的所有权转移给它。

1
2
3
4
5
6
7
8
Fun = fun() -> receive after infinity -> ok end end.
% #Fun<erl_eval.20.54118792>
SomeProcess = spawn(Fun).
% <0.52.0>
Products = ets:new(products, [public]).
% 16402
ets:give_away(Products, SomeProcess, []).
% true

下一步我们将装载我们的“产品”进入表里。

在我们这个例子里,我们只是创建一个产品,用一个二进制数据当做产品的名字并用一个整数当做“共同世界货币里的价格”。

1
2
3
4
[[ ets:insert(Products, {integer_to_binary(X), X}) || X <- lists:seq(1, 100) ]].
% [[true,true,true,true,true,true,true,true,true,true,true,
% true,true,true,true,true,true,true,true,true,true,true,true,
% true,true,true,true,true|...]]

和我们在上星期一样,我们可以手工创建一个元组列表到匹配规则里去运行我们的查询,说是获取小于10CWC的产品。

1
2
3
ets:select(Products, [{{'$1', '$2'}, [{'<', '$2', 10}], ['$1']}]).
% [<<"8">>,<<"6">>,<<"5">>,<<"3">>,<<"7">>,<<"1">>,<<"4">>,
% <<"9">>,<<"2">>]

我们也可以查询到那些大于10CWC并且小于25CWC的产品名称。

1
2
3
4
ets:select(Products, [{{'$1', '$2'}, [{'>', '$2', 10}, {'<', '$2', 25}], ['$1']}]).
% [<<"11">>,<<"15">>,<<"23">>,<<"20">>,<<"21">>,<<"14">>,
% <<"12">>,<<"13">>,<<"16">>,<<"19">>,<<"17">>,<<"18">>,
% <<"22">>,<<"24">>]

但是这样并不一定清晰明了,因为我们正在使用元组里的元素的数值以及在它们里面元组列表的元组列表。

这个时候 ets:fun2ms/1 来解救我们。

ets:fun2ms/1 的入参是一个函数并且将把这个函数转换成一个匹配规则。

这就允许我们写一个函数,这个函数入参是一个产品和价格组成的元组,然后它返回价格小于10的产品。

1
2
ets:fun2ms(fun({Product, Cost}) when Cost < 10 -> Product end).
% [{{'$1','$2'},[{'<','$2',10}],['$1']}]

我们也能在传递给 ets:fun2ms/1 的这个函数的判断分支里用一个组合检查,相当于分支类型语句,

1
2
3
4
5
Between_25_And_35_CWC = ets:fun2ms(fun({Product, Cost}) when Cost > 25, Cost < 35 -> Product end).
% [{{'$1','$2'},[{'>','$2',25},{'<','$2',35}],['$1']}]
ets:select(Products, Between_25_And_35_CWC).
% [<<"30">>,<<"33">>,<<"32">>,<<"29">>,<<"28">>,<<"26">>,
% <<"34">>,<<"27">>,<<"31">>]

或者也可以是类型分支语句。

虽然这个函数有用,但是它还是有它的限制,因为这是在函数上解析转换,所以你不能像在普通函数上一样用任何东西。

1
2
3
ets:fun2ms(fun({Product, Cost}) when Cost > 90 -> lists:reverse(binary:bin_to_list(Product)) end).
% Error: Unknown error code {122,lists,reverse}
% {error,transform_error}

但是,匹配规则的结果部分也是不支持任何高级函数功能的。

1
2
3
4
ets:select(Products, [{{'$1', '$2'}, [{'<', 90, '$2'}], [binary:bin_to_list('$1')]}]).
% ** exception error: bad argument
% in function binary:bin_to_list/1
% called as binary:bin_to_list('$1')

即使有这些限制,ets:fun2ms/1 依然帮助我们做了更富表现力的ETS查询。我们不仅能用有意义的变量名替代那些 $X 变量来引用一个函数,也用判断分支语句来替代判断元组,而且我们也可以在我们的结果里用这些变量名同时做基本的格式化作为函数的一部分。

请确认在下周回来,因为我们将继续研究 ets:select 的不同版本。

原文链接: https://www.proctor-it.com/erlang-thursday-ets-match_specs-and-functions/