文档结构  
翻译进度:已翻译     翻译赏金:0 元 (?)    ¥ 我要打赏

上星期的Erlang Thursday我们继续研究 ets:select/2 并且用 ets:fun2ms/1生成匹配规则来和它配合使用。

这个星期我们将看看ets模块提供的select函数的其它版本。

还是老样子,我们将设置好我们新的ETS表的环境,以便我们的shell崩溃了我们的表不会丢失。

Fun = fun() -> receive after infinity -> ok end end.
% #Fun<erl_eval.20.54118792>
SomeProcess = spawn(Fun).
% <0.52.0>
TestTable = ets:new(ets_table, [public]).
% 16402
ets:give_away(TestTable, SomeProcess, []).
% true

 

第 1 段(可获 0.79 积分)

接下来我们将装载我们的测试ETS表,它是一些测试“产品”。为了例子的简单,我们将仅用一个数字代表一个产品id,然后用一个100以内的随机整数加上0.99作为价格。

[[ets:insert(TestTable, {ProductId, random:uniform(100) + 0.99})
  || ProductId <- lists:seq(1, 10000) ]].
% [[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|...]]

我们将创建一个匹配规则(价格在19.99至30之间)来查找数据。

第 2 段(可获 0.73 积分)
ProductsInTheTwenties = ets:fun2ms(fun({Product, Price})
                                     when Price >= 19.99 andalso Price < 30
                                     -> {Product, Price}
                                   end).
% [{{'$1','$2'},
%   [{'andalso',{'>=','$2',19.99},{'<','$2',30}}],
%   [{{'$1','$2'}}]}]

如果我们用 ets:select/2 和上面这个匹配规则在我们的表上,我们在一个查询里得到所有结果就和前面我们看到的一样。

ets:select(TestTable, ProductsInTheTwenties).
% [{4351,29.99},
%  {635,19.99},
%  {6005,20.99},
%  {3742,27.99},
%  {5956,29.99},
%  {3753,28.99},
%  {6653,25.99},
%  {5151,28.99},
%  {2693,27.99},
%  {4253,21.99},
%  {7636,23.99},
%  {1935,19.99},
%  {9044,22.99},
%  {7797,22.99},
%  {2147,23.99},
%  {2574,26.99},
%  {7575,29.99},
%  {2130,28.99},
%  {4908,27.99},
%  {2218,22.99},
%  {9848,21.99},
%  {7632,26.99},
%  {3562,21.99},
%  {3130,27.99},
%  {575,26.99},
%  {4622,28.99},
%  {5678,25.99},
%  {4022,...},
%  {...}|...]

 

第 3 段(可获 0.31 积分)

不过ets模块也给我们一个限制结果集的方式如果我们愿意的话,用 ets:select/3 并传入一个要一次返回结果数的限制。

那么我们来用 ets:select/3 并给它的限制是10,然后看看结果是什么。

ets:select(TestTable, ProductsInTheTwenties, 10).
% {[{9027,27.99},
%   {7347,29.99},
%   {7282,20.99},
%   {9386,24.99},
%   {5415,25.99},
%   {4032,29.99},
%   {8105,25.99},
%   {4634,24.99},
%   {1275,20.99},
%   {234,20.99}],
%  {16402,576,10,<<>>,[],0}}

我们的结果是一个元组而不是一个结果的列表。第一个元组元素是一个我们期望的10个结果组成的列表,第二个元素是一个奇怪的元组,我们查阅官方文档中 ets:select/3 的描述,这个奇怪的元组表示一个概念:continuation 。

第 4 段(可获 1.2 积分)

所以我们再运行我们的查询,这次我们把结果绑定到变量。

{Results, Continuation} = ets:select(TestTable, ProductsInTheTwenties, 10).
% {[{9027,27.99},
%   {7347,29.99},
%   {7282,20.99},
%   {9386,24.99},
%   {5415,25.99},
%   {4032,29.99},
%   {8105,25.99},
%   {4634,24.99},
%   {1275,20.99},
%   {234,20.99}],
%  {16402,576,10,<<>>,[],0}}

现在我们有了这个continuation,不过它是什么?它对我们来说有什么用?

简而言之,它可以被认为是一个不可变的书签。它不仅表示我们在查询结果的哪一页,也表示我们正在读的内容(我们的查询)。

第 5 段(可获 0.8 积分)

它允许我们把这个continuation传给 ets:select/1 ,就能快速获取我们前面看过的结果内容。

ets:select(Continuation).
% {[{2533,24.99},
%   {1357,22.99},
%   {564,21.99},
%   {9086,22.99},
%   {5265,25.99},
%   {4030,22.99},
%   {2802,25.99},
%   {8254,27.99},
%   {7088,26.99},
%   {3062,27.99}],
%  {16402,960,10,<<>>,[{6792,29.99},{9295,29.99}],2}}

因为它是我们的特殊的不可变的书签,每次我们用这个书签它都带我们到这同样书的相同的地方,并且我们仅能读到我们原先设置的每页最大纪录数。

第 6 段(可获 0.83 积分)

所以不管我们在我们同一个continuation上调用多少次 ets:select/1 ,每次我们都将获得相同的结果。

ets:select(Continuation).
% {[{2533,24.99},
%   {1357,22.99},
%   {564,21.99},
%   {9086,22.99},
%   {5265,25.99},
%   {4030,22.99},
%   {2802,25.99},
%   {8254,27.99},
%   {7088,26.99},
%   {3062,27.99}],
%  {16402,960,10,<<>>,[{6792,29.99},{9295,29.99}],2}}
ets:select(Continuation).
% {[{2533,24.99},
%   {1357,22.99},
%   {564,21.99},
%   {9086,22.99},
%   {5265,25.99},
%   {4030,22.99},
%   {2802,25.99},
%   {8254,27.99},
%   {7088,26.99},
%   {3062,27.99}],
%  {16402,960,10,<<>>,[{6792,29.99},{9295,29.99}],2}}
ets:select(Continuation).
% {[{2533,24.99},
%   {1357,22.99},
%   {564,21.99},
%   {9086,22.99},
%   {5265,25.99},
%   {4030,22.99},
%   {2802,25.99},
%   {8254,27.99},
%   {7088,26.99},
%   {3062,27.99}],
%  {16402,960,10,<<>>,[{6792,29.99},{9295,29.99}],2}}

 

第 7 段(可获 0.25 积分)

而如果我们仔细看结果的元组,我们看到得到一个不同的下一个continuation的元组。

{SecondResults, SecondContinuation} = ets:select(Continuation).
% {[{2533,24.99},
%   {1357,22.99},
%   {564,21.99},
%   {9086,22.99},
%   {5265,25.99},
%   {4030,22.99},
%   {2802,25.99},
%   {8254,27.99},
%   {7088,26.99},
%   {3062,27.99}],
%  {16402,960,10,<<>>,[{6792,29.99},{9295,29.99}],2}}

我们可以用这个新的continuation用在我们下一次调用 ets:select/1 上,来得到下一个结果集和另一个continuation。

第 8 段(可获 0.58 积分)
ets:select(SecondContinuation).
% {[{8569,19.99},
%   {1805,28.99},
%   {6819,23.99},
%   {9313,28.99},
%   {9527,27.99},
%   {1737,29.99},
%   {700,26.99},
%   {142,25.99},
%   {6792,29.99},
%   {9295,29.99}],
%  {16402,513,10,<<>>,[],0}}

如果我们在获取完结果集后再执行一次查询,我们得到一个 ‘$end_of_table’ 原子。

ets:select(TestTable, [{{'$1', '$2'}, [{'<', '$2', 0}], ['$$']}], 10).
% '$end_of_table'

指定一个限制并有一个continuation的能力也可以用在 ets:match/3 和 ets:match/1 上,同时也可以用在 ets:match_object/3 和 ets:match_object/1 上。

第 9 段(可获 0.53 积分)

下星期,我们将继续研究ets模块里的不同select函数,同时看看它们的行为方式和有序集合,将比较一下 select 函数和 select_reverse函数的不同,也研究一下如果我们当我们用一个continuation的时候在结果集里插入一些数据,continuation将会怎样。

第 10 段(可获 0.59 积分)

文章评论