欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Erlang模块ets翻译

程序员文章站 2022-03-02 09:05:05
概要: 内置的存储 描述: 这个模块是Erlang内置存储BIFs的接口。这些提供了在Erlang运行时系统中存储大量数据的能力,并且能够对数据进行持续的访问时间。(在ordered_set的情况下,参见下面,访问时间与存储的对象数量的对数成正比。) 数据被组织成一组动态表,可以存储元组。每个表都是 ......

 

概要
  内置的存储

描述
  这个模块是Erlang内置存储BIFs的接口。这些提供了在Erlang运行时系统中存储大量数据的能力,并且能够对数据进行持续的访问时间。(在ordered_set的情况下,参见下面,访问时间与存储的对象数量的对数成正比。)
  数据被组织成一组动态表,可以存储元组。每个表都是由一个进程创建的。当进程终止时,表将被自动销毁。每个表在创建时都具有访问权限。
  表分为四种不同的类型:set、ordered_set、bag和duplicate_bag。set或ordered_set表只能有一个与每个键相关联的对象。bag或duplicate_bag可以有许多与每个键相关联的对象。
  在一个Erlang节点存储的表的数量是有限的。当前的缺省限制大约是1400个表。在启动Erlang运行时系统之前,可以通过设置环境变量 ERL_MAX_ETS_TABLES 来增加上限(例如,使用-env选项到erl/werl)。实际的限制可能比指定的要稍微高一些,但永远不会降低。
  注意,表中没有自动垃圾收集。即使没有任何进程的引用,也不会自动销毁,除非所有者进程终止。它可以通过 delete/1 显式地销毁。默认的所有者是创建表的过程。表所有权可以通过使用继承人选项或显式地调用 give_away/3 来转移。

  一些实现细节:
    在当前的实现中,每一个对象插入和查找操作都会产生一个对象的副本;
    '$end_of_table' 不应该用作键,因为在使用first/next时,这个原子被用来标记表的末尾。

  同样值得注意的是匹配和比较相等之间的细微差别,这是由不同的表类型 set 和 ordered_set 所演示的。两个Erlang项匹配,如果它们是相同类型且具有相同的值,那么1匹配1,而不是1.0(1.0是浮点数而不是整数)。两个Erlang的项比较相等,要么它们是相同类型和值的,要么两者都是数值类型并扩展到相同的值,那么1比1和1.0都是一样的。ordered_set在Erlang项顺序上工作,在整数和浮点之间没有定义的顺序,它扩展到相同的值,因此键1和键1.0在ordered_set表中被认为是相等的。

故障
  一般来说,如果任何参数的格式是错误的,那么下面的函数将会退出,如果表标识符是无效的,或者由于表访问权限而拒绝操作(protected or private)。

并发
  这个模块为并发访问提供了一些有限的支持。对单个对象的所有更新都保证是原子性的和隔离性的。这意味着对单个对象的更新操作要么成功,要么完全失败,完全没有任何影响(原子性)。其他进程(隔离性)也不能看到更新的中间结果。一些更新了几个对象的函数表明,它们甚至可以保证整个操作的原子性和隔离性。在数据库术语中,隔离级别可以被看作是“可序列化的”,就好像所有的隔离操作都是串行地执行的,一个接一个地以严格的顺序执行。

匹配规范
  有些函数使用匹配规范,match_spec。在select/2中给出了一个简短的解释。有关详细描述,请参阅ERTS用户指南中的“Erlang中的匹配规范”一章。

数据类型
access() = public | protected | private
continuation()
  select/1,3, select_reverse/1,3, match/1,3, and match_object/1,3 使用不透明的 continuation。
match_spec() = [{match_pattern(), [term()], [term()]}]
  匹配规范,见上文。
comp_match_spec()
  一个编译的匹配规范。
match_pattern() = atom() | tuple()
tab() = atom() | tid()
tid()
  一个表标识符,new/2 返回的。
type() = set | ordered_set | bag | duplicate_bag

导出
all() -> [Tab]
  Types:
    Tab = tab()
  返回节点上所有表的列表。命名表由它们的名称给出,未命名的表由它们的表标识符给出。

delete(Tab) -> true
  Types:
    Tab = tab()
  删除整个表 Tab。

delete(Tab, Key) -> true
  Types:
    Tab = tab()
    Key = term()
  从表 Tab中删除所有带有键 Key的对象。

delete_all_objects(Tab) -> true
  Types:
    Tab = tab()
  删除ETS表 Tab中的所有对象。这个操作被保证是原子性的和隔离性的。

delete_object(Tab, Object) -> true
  Types:
    Tab = tab()
    Object = tuple()
  从ETS表中删除确切的对象Object,留下具有相同键的对象,但其他不同(对于类型 bag有用)。在duplicate_bag中,对象的所有实例都将被删除。

file2tab(Filename) -> {ok, Tab} | {error, Reason}
  Types:
    Filename = file:name()
    Tab = tab()
    Reason = term()
  读取tab2file/2或tab2file/3生成的文件,并创建相应的表 Tab。
  相当于 file2tab(Filename,[])。

file2tab(Filename, Options) -> {ok, Tab} | {error, Reason}
  Types:
    Filename = file:name()
    Tab = tab()
    Options = [Option]
    Option = {verify, boolean()}
    Reason = term()
  读取tab2file/2或tab2file/3生成的文件,并创建相应的表 Tab。

  目前唯一受支持的选项是 {verify,boolean()}。如果验证被打开(通过指定 {verify,true}),该函数将利用文件中存在的任何信息来断言信息没有被破坏。这是如何完成的,取决于使用 tab2file/3编写的 extended_info。
  如果文件中没有 extended_info,并且 {verify,true} 是指定的,当转储开始时,所编写的对象的数量将与原始表的大小进行比较。如果表是 public,并且在表被转储到文件时,则会使验证失败。为了避免这种类型的问题,要么在同步更新时不验证转储文件,要么使用 {extended_info, [object_count]} 选项到 tab2file/3,它将文件中的信息扩展到实际编写的对象的数量。
  如果验证被打开,并且该文件是用 {extended_info, [md5sum]} 编写的,那么读取文件的速度会更慢,消耗的CPU时间也会大大增加。

first(Tab) -> Key | '$end_of_table'
  Types:
    Tab = tab()
    Key = term()
  返回表 Tab中的第一个键 Key。如果该表是 ordered_set类型,那么Erlang项顺序中的第一个键将被返回。如果表是任何其他类型的,那么根据该表的内部顺序将返回第一个键。如果表是空的,则返回 '$end_of_table'。
  使用 next/2在表格中找到后续的键。

foldl(Function, Acc0, Tab) -> Acc1
  Types:
    Function = fun((Element :: term(), AccIn) -> AccOut)
    Tab = tab()
    Acc0 = Acc1 = AccIn = AccOut = term()
  如果表是空的,则返回Acc0。这个函数类似于 lists:foldl/3。除了类型ordered_set的表之外,表中元素的顺序是不确定的,它们从头到尾被遍历。
  如果函数将对象插入到表中,或者另一个进程将对象插入到表中,那么这些对象可能(取决于键顺序)包含在遍历中。

foldr(Function, Acc0, Tab) -> Acc1
  Types:
    Function = fun((Element :: term(), AccIn) -> AccOut)
    Tab = tab()
    Acc0 = Acc1 = AccIn = AccOut = term()
  如果表是空的,则返回Acc0。这个函数类似于 lists:foldr/3。表中元素的顺序是不确定的,除了类型ordered_set的表之外,它们从尾到头被遍历。
  如果函数将对象插入到表中,或者另一个进程将对象插入到表中,那么这些对象可能(取决于键顺序)包含在遍历中。

from_dets(Tab, DetsTab) -> true
  Types:
    Tab = tab()
    DetsTab = dets:tab_name()
  在已经打开的Dets表中,填满已经创建的ETS表,这个表名为DetsTab。除非覆盖,否则ETS表的现有对象将被保留。
  如果任何表不存在或dets表不打开,就会抛出一个badarg错误。

fun2ms(LiteralFun) -> MatchSpec
  Types:
    LiteralFun = function()
    MatchSpec = match_spec()
  伪函数通过 parse_transform的方式将 LiteralFun翻译成参数,作为参数转换成match_spec在函数调用中。“literal”是指,在文本中,需要将其作为函数的参数来写,它不能被保存在一个变量中,而这个变量又被传递给函数。
  解析转换是在模块 ms_transform中实现的,源代码必须包含文件ms_transform.hrl。在STDLIB中为这个伪函数工作。如果不能在源代码中包含hrl文件,将导致运行时错误,而不是编译时的错误。include文件最容易包括添加行-include(“stdlib/include/ms transform.hrl”).到源代码。
  fun是非常有限的,它只能接受一个单一的参数(匹配的对象):一个唯一的变量或一个元组。它需要使用is_ 关卡测试。在match_spec中不能使用语言结构(例如,if,case,receive等)是不允许的。
  返回值是产生的match_spec。
例子:
1> ets:fun2ms(fun({M,N}) when N > 3 -> M end).
[{{'$1','$2'},[{'>','$2',3}],['$1']}]
来自环境的变量可以被导入,因此它可以工作:
2> X=3.
3
3> ets:fun2ms(fun({M,N}) when N > X -> M end).
[{{'$1','$2'},[{'>','$2',{const,3}}],['$1
导入的变量将被 match_spec 常量表达式替换,这与Erlang funs的静态作用域一致。然而,本地或全局的函数调用不能处于fun的关卡或主体中。当然,对构建match_spec函数的内建调用是允许的:
4> ets:fun2ms(fun({M,N}) when N > X, is_atomm(M) -> M end).
Error: fun containing local Erlang function calls
('is_atomm' called in guard) cannot be translated into match_spec
{error,transform_error}
5> ets:fun2ms(fun({M,N}) when N > X, is_atom(M) -> M end).
[{{'$1','$2'},[{'>','$2',{const,3}},{is_atom,'$1'}],['$1']}]
  从这个例子中可以看出,这个函数也可以从 shell中调用。当从shell中使用时,fun也需要在调用中。在shell案例中使用的其他方法是使用parse_transform,但或多或少是相同的限制(例外是记录,因为它们不是由shell处理的)。
警告:
  如果 parse_transform没有被应用到调用这个伪函数的模块中,那么调用将在运行时失败(带有badarg)。模块ets实际上导出了一个带有这个名称的函数,但是除了在shell中使用函数时,它永远不应该被调用。如果通过包含 ms_transform.hrl 来正确地应用parse_transform,编译后的代码永远不会调用这个函数,但是函数调用被一个文字匹配规范所取代。
  要了解更多信息,请参见mstransform(3)。

give_away(Tab, Pid, GiftData) -> true
  Types:
    Tab = tab()
    Pid = pid()
    GiftData = term()
  使进程Pid成为表 Tab的新所有者。如果成功,将消息{“ETS-TRANSFER”,Tab,FromPid,GiftData}发送给新所有者。
  进程Pid必须是活的,本地的,而不是表的所有者。调用过程必须是表所有者。
  请注意,give_away 并不会影响到表的继承人选项。例如,表所有者可以将继承人设置为自己,将表放在一边,然后将其取回,以防接收方终止。

i() -> ok
  在tty显示所有ETS表的信息。

i(Tab) -> ok
  Types:
    Tab = tab()
  在tty上浏览表Tab。

info(Tab) -> InfoList | undefined
  Types:
    Tab = tab()
    InfoList = [InfoTuple]
    InfoTuple = {compressed, boolean()}
    | {heir, pid() | none}
    | {keypos, integer() >= 1}
    | {memory, integer() >= 0}
    | {name, atom()}
    | {named_table, boolean()}
    | {node, node()}
    | {owner, pid()}
    | {protection, access()}
    | {size, integer() >= 0}
    | {type, type()}
  将表 Tab的信息返回为元组列表。如果Tab有正确的表标识符类型,但不引用现有的ETS表,则返回未定义。如果Tab不是正确的类型,那么这个函数就会因为badarg而失败。
{compressed, boolean()}
  表示表是否被压缩。
{heir, pid() | none}
  表的继承者的pid,如果没有继承者,则为none。
{keypos, integer() >= 1}
  键位置。
{memory, integer() >= 0
  分配给表的空间(以字为单位)。
{name, atom()}
  表名。
{named_table, boolean()}
  表明表是否被命名。
{node, node()}
  存放表的节点。这个字段不再有意义,因为表不能从其他节点访问。
{owner, pid()}
  表所有者的pid。
{protection, access()}
  表访问的权限。
{size, integer() >= 0
  在表中插入的对象的数量。
{type, type()}
  表的类型。

info(Tab, Item) -> Value | undefined
  Types:
    Tab = tab()
    Item = compressed
    | fixed
    | heir
    | keypos
    | memory
    | name
    | named_table
    | node
    | owner
    | protection
    | safe_fixed
    | size
    | stats
    | type
    Value = term()
  返回表的相关信息,如果Tab不引用现有的ETS表,则返回未定义的信息。如果Tab不是正确的类型,或者如果Item不是允许的值之一,那么这个函数就会因为badarg而失败。
  警告:
    在R11B和更早的时候,这个函数不会失败,但是对于条目的无效值返回undefined。
  对info/1,除了{Item,Value}定义,下面的项也被允许:
  Item=fixed, Value=boolean()
  表示表是否由任何进程固定。
  Item=safe_fixed, Value={FirstFixed,Info}|false 如果表是使用safe_fixtable/2修复的,那么调用返回一个元组,其中FirstFixed是表第一次被进程修复的时间,它可能是现在修复的进程之一。Info是一个可能的空列表,它是一个元组{Pid,RefCount},一个元组,每个进程现在都是固定的。RefCount是参考计数器的值,它记录了进程中表被修复了多少次。
  如果表从未被修复,则调用返回false。
  Item=stats, Value=tuple()
  返回关于OTP测试套件使用的内部格式的set、bag和duplicate_bag表的内部统计信息。不适合生产使用。

init_table(Tab, InitFun) -> true
  Types:
    Tab = tab()
    InitFun = fun((Arg) -> Res)
    Arg = read | close
    Res = end_of_input | {Objects :: [term()], InitFun} | term()
  用调用输入函数InitFun创建的对象来替换表Tab的现有对象,如下所示。这个函数是为了与dets模块兼容而提供的,它并不比使用ets:insert/2来填充表效率更高。
  当被调用时,读取函数InitFun被假定为返回end_of_input,当没有更多的输入,或{Objects,Fun},Objects是一个对象列表,而fun是一个新的输入函数。任何其他值的值都作为错误,返回{error,{init_fun,Value}}。每一个输入函数都将被调用一次,如果出现错误,最后一个函数会在参数关闭时被调用,它的应答将被忽略。
  如果表的类型被设定,并且有多个对象和一个给定的键,那么就会选择其中一个对象。这并不一定是在输入函数返回的对象序列中给定键的最后一个对象。这也适用于存储在类型bag表中的重复对象。

insert(Tab, ObjectOrObjects) -> true
  Types:
    Tab = tab()
    ObjectOrObjects = tuple() | [tuple()]
  将列表ObjectOrObjects中的对象或所有对象插入到表Tab中。如果表是一个set,并且插入的对象的键与表中任何对象的键相匹配,那么旧对象将被替换。如果表是一个ordered_set,并且插入的对象的键与表中的任何对象的键相等,那么旧对象也会被替换。如果列表中包含了不止一个带有匹配键的对象,并且该表是一个set,那么就会插入一个对象,其中一个没有定义。对于ordered_set也是一样的,但是如果键比较相等,也会发生同样的事情。
  整个操作被保证是原子性的和隔离性的,即使插入了一个对象列表。

insert_new(Tab, ObjectOrObjects) -> boolean()
  Types:
    Tab = tab()
    ObjectOrObjects = tuple() | [tuple()]
  这个函数的工作原理与insert/2完全相同,除了用相同的键重写对象(在set或ordered_set的情况下),或者在表中已经存在的键中添加更多的对象(在bag和duplicate_bag的情况下),它只是返回false。如果ObjectOrObjects是一个列表,那么这个函数在插入任何东西之前都会检查每个键。如果列表中没有列出所有的键,则不会插入任何东西。与insert/2一样,整个操作都保证是原子性的和隔离性的。

is_compiled_ms(Term) -> boolean()
  Types:
    Term = term()
  这个函数用来检查一个术语是否是有效编译的match_spec。编译的match_spec是一种不透明的数据类型,不能在Erlang节点之间发送,也不能存储在磁盘上。任何试图创建一个编译的match_spec的外部表示的尝试都会导致一个空的二进制(<<>>)。下面的表达式,作为一个例子:
  ets:is_compiled_ms(ets:match_spec_compile([{'_',[],[true]}])).
  会产生true:
  MS = ets:match_spec_compile([{'_',[],[true]}]),
  Broken = binary_to_term(term_to_binary(MS)),
  ets:is_compiled_ms(Broken).
  会产生错误,因为变量被破坏将包含一个经过编译的match_spec,它已经通过外部表示。
  请注意:
    编译匹配规范没有外部表示的事实是出于性能考虑。它可能会在将来的版本中发生变化,而这个接口仍然会保持向后兼容的原因。

last(Tab) -> Key | '$end_of_table'
  Types:
    Tab = tab()
    Key = term()
  根据ordered_set类型的表Tab返回最后一个键Key。如果表是任何其他类型的,那么该函数就是first/2的同义词。如果表为空,则返回“$end_of_table”。
  使用prev/2来查找表中的前键。

lookup(Tab, Key) -> [Object]
  Types:
    Tab = tab()
    Key = term()
    Object = tuple()
  在表Tab中返回所有带有键key的对象列表。
  在set、bag和duplicate_bag的情况下,只有当给定的键与表中对象的键相匹配时,才会返回一个对象。然而,如果表示一个ordered_set,那么如果给定的键比表中的对象的键相等,则返回一个对象。区别和=:=和==一样。作为一个例子,你可以在ordered_set中插入一个带有integer() 1的对象,并将该对象作为lookup/2的键,获得查找的结果;和用float() 1.0 作为key查找。
  如果表是类型set或ordered_set,则函数返回空列表或带有一个元素的列表,因为不能有多个具有相同键的对象。如果表是类型bag或duplicate_bag,则该函数返回任意长度的列表。
  注意对象插入的时间顺序被保留;用给定的键插入的第一个对象将首先出现在结果列表中,以此类推。
  在表中插入和查找时间,set、bag和duplicate_bag是常量,不管表的大小。对于ordered_set的数据类型,时间与对象数量的对数成比例。

lookup_element(Tab, Key, Pos) -> Elem
  Types:
    Tab = tab()
    Key = term()
    Pos = integer() >= 1
    Elem = term() | [term()]
  如果表Tab是类型set或ordered_set,则该函数返回带有键key的位置为Pos的元素。
  如果表是bag或duplicate_bag,那么函数将返回一个列表,其中包含每个对象的Pos位置的元素。
  如果没有键Key的对象,则该函数将以理由badarg退出。
  一方面,set、bag和duplicate_bag之间的区别,另一方面是ordered_set,关于ordered_set的认为键在比较相等时是相等的,而其它表类型只在匹配时才相等,lookup_element和lookup自然适用。

match(Tab, Pattern) -> [Match]
  Types:
    Tab = tab()
    Pattern = match_pattern()
    Match = [term()]
  将表Tab中的对象与模式Pattern匹配。
  模式是一个项,可能包含:
  绑定部分(Erlang项),
  它与任何Erlang项相匹配,
  模式变量:$N,N=0,1,……
  该函数为每一个匹配对象返回一个含有其中元素的列表,其中每个元素都是模式变量绑定的有序列表。一个例子:
6> ets:match(T, '$1'). % Matches every object in the table
[[{rufsen,dog,7}],[{brunte,horse,5}],[{ludde,dog,5}]]
7> ets:match(T, {'_',dog,'$1'}).
[[7],[5]]
8> ets:match(T, {'_',cow,'$1'}).
[]
  如果在模式中指定了键,那么匹配是非常有效的。如果没有指定键,也就是说,如果它是一个变量或一个下划线,那么必须搜索整个表。如果表非常大,搜索时间可能会很大。
  在ordered_set类型的表中,结果与first/next遍历的顺序是相同的。

match(Tab, Pattern, Limit) -> {[Match], Continuation} | '$end_of_table'
  Types:
    Tab = tab()
    Pattern = match_pattern()
    Limit = integer() >= 1
    Match = [term()]
    Continuation = continuation()
  跟 ets:match/2 一样,都是根据匹配模式 Pattern 匹配表 Tab 里的对象数据,但是 ets:match/3 只返回 Limit 条的匹配数据。返回结果里还同时返回变量 Continuation,这可作为下一次调用 ets:match/1 方法的参数来获取下一批的匹配数据,这比使用 ets:first/1 和 ets:next/2 方法遍历获取表里的对象数据会更快,更有效率。
  如果表是空的,则返回$end_of_table。

match(Continuation) -> {[Match], Continuation} | '$end_of_table'
  Types:
    Match = [term()]
    Continuation = continuation()
  继续从 ets:match/3 方法开始匹配数据,调用 ets:match/3 方法跟匹配数据一起返回的变量 Continuation 可用在下一次调用这个函数来获取下一批的匹配数据。
  当表中没有更多的对象时,“$end_of_table”就会返回。

match_delete(Tab, Pattern) -> true
  Types:
    Tab = tab()
    Pattern = match_pattern()
  从表Tab中删除与模式Pattern匹配的所有对象。有关模式的描述,请参阅match/2。

match_object(Tab, Pattern) -> [Object]
  Types:
    Tab = tab()
    Pattern = match_pattern()
    Object = tuple()
  将表Tab中的对象与模式Pattern匹配。有关模式的描述,请参阅match/2。该函数返回与模式匹配的所有对象的列表。
  如果在模式中指定了键,那么匹配是非常有效的。如果没有指定键,也就是说,如果它是一个变量或一个下划线,那么必须搜索整个表。如果表非常大,搜索时间可能会很大。
  在ordered_set类型的表格中,结果与first/next遍历的顺序是相同的。

match_object(Tab, Pattern, Limit) -> {[Match], Continuation} | '$end_of_table'
  Types:
    Tab = tab()
    Pattern = match_pattern()
    Limit = integer() >= 1
    Match = [term()]
    Continuation = continuation()
  跟 ets:match_object/2 一样,都是根据匹配模式 Pattern 匹配表 Tab 里的对象数据,但是 ets:match_object/3 只返回 Limit 条的匹配数据。返回结果里还同时返回变量 Continuation,这可作为下一次调用 ets:match_object/1 方法的参数来获取下一批的匹配数据,这比使用 ets:first/1 和 ets:next/2 方法遍历获取表里的对象数据更快,更有效率。
  如果表是空的,则返回$end_of_table。

match_object(Continuation) -> {[Match], Continuation} | '$end_of_table'
  Types:
    Match = [term()]
    Continuation = continuation()
  继续从 ets:match_object/3 方法开始匹配数据,调用 ets:match_object/3 方法跟匹配数据一起返回的变量 Continuation 可用在下一次调用这个函数来获取下一批的匹配数据。
  当表中没有更多的对象时,“$end_of_table”就会返回。

match_spec_compile(MatchSpec) -> CompiledMatchSpec
  Types:
    MatchSpec = match_spec()
    CompiledMatchSpec = comp_match_spec()
  这个函数把一个匹配规范 MatchSpec 转换为一个可用于被 ets:match_spec_run/2 在后续调用的内部表示形式。转换后的内部形式"不透明"的一个数据,它并不能转回为其原来的外部数据格式,并且完好无损的转回(这意味着它不可以发送到另一节点的进程上,并且仍然是一个有效的已编译的匹配规范,或存储在磁盘上也是不行)。
  如果“MatchSpec”这个项不能被编译(不代表有效的MatchSpec),就会抛出一个badarg错误。
注意:
  这个函数在普通代码中使用有限,dets使用它来执行dets:select操作。

match_spec_run(List, CompiledMatchSpec) -> list()
  Types:
    List = [tuple()]
    CompiledMatchSpec = comp_match_spec()
  这个函数是用一个已编译的匹配规范对一个元组形式的列表进行指定匹配,参数 CompiledMatchSpec 是函数 ets:match_spec_compile/1 调用产生的匹配规范,因此是该函数想用的匹配规范的内部表示形式。
  该匹配操作会对列表里的每一个元素进行匹配操作,并返回一个包含所有结果的列表。如果列表里的一个元素并不符合匹配,将不会返回任何数据。因此返回的结果列表的长度有可能会小于参数列表的长度。
  下面的两个调用将给出相同的结果(但肯定不是相同的执行时间。)
Table = ets:new...
MatchSpec = ....
% The following call...
ets:match_spec_run(ets:tab2list(Table),
ets:match_spec_compile(MatchSpec)),
% ...will give the same result as the more common (and more efficient)
ets:select(Table,MatchSpec),
注意:
  这个函数在普通代码中使用有限,它被dets用来执行dets:select操作和Mnesia事务中。

member(Tab, Key) -> boolean()
  Types:
    Tab = tab()
    Key = term()
  像lookup/2一样工作,但不返回对象。如果表格中的一个或多个元素有键Key,则返回true;反之则返回false。

new(Name, Options) -> tid() | atom()
  Types:
  Name = atom()
  Options = [Option]
  Option = Type
  | Access
  | named_table
  | {keypos, Pos}
  | {heir, Pid :: pid(), HeirData}
  | {heir, none}
  | Tweaks
  Type = type()
  Access = access()
  Tweaks = {write_concurrency, boolean()}| {read_concurrency, boolean()}| compressed
  Pos = integer() >= 1
  HeirData = term()
  创建一个新的表并返回一个表标识符,它可以在后续操作中使用。表格标识符可以被发送到其他进程,以便在一个节点内的不同进程之间共享一张表。
  参数选项是一个原子列表,它指定表类型、访问权限、键位置,以及表是否被命名。如果遗漏了一个或多个选项,则使用默认值。这意味着不指定任何选项与指定[set, protected, {keypos,1}, {heir,none}, {write_concurrency,false}, {read_concurrency,false}]相同。
  set
  设置表是一个set表——一个键,一个对象,对象之间没有顺序。这是默认的表类型。
  ordered_set
  表是一个orderedset表——一个键,一个对象,在Erlang项顺序中排序,这是由<and>操作符所暗示的顺序。这种类型的表在某些情况下的行为与其他类型的表有所不同。最明显的是,ordered_set表在比较相等时,将键视为相等,而不仅仅是匹配时。这意味着对于ordered_set来说,integer()1和float()1.0被认为是相等的。这也意味着用于查找元素的键不一定与返回的元素中的键相匹配,如果float()和integer()的键混合在表的键中。
  bag
  这个表是一个bag表,它可以有很多对象,但是每个对象只有一个实例,每个键。
  duplicate_bag
  这个表是一个duplicate_bag表,它可以有许多对象,包括同一对象的多个副本,每个键。
  public
  任何进程都可以读或写到表中。
  protected
  所有者进程可以读取和写入表。其他进程只能读取表。这是存取权的默认设置。
  private
  只有所有者进程可以读写表。
  named_table
  如果这个选项存在,名称Name与表标识符相关联。然后,可以在后续操作中使用名称代替表标识符。
  {keypos,Pos}
  在存储的元组中,应该使用哪个元素作为键。默认情况下,它是第一个元素,即Pos=1。然而,这并不总是合适的。特别地,我们不希望第一个元素成为键,如果我们想把Erlang记录存储在一张表中。
  注意,表中存储的任何元组必须至少有Pos数量的元素。
  {heir,Pid,HeirData} | {heir,none}
  设定一个进程作为继承者。如果所有者终止,继承者将继承该表。当这种情况发生时,将会把消息{'ETS-TRANSFER',tid(),FromPid,HeirData} 发送给继承者。继承者必须是一个本地进程。默认的继承者是none,当所有者终止时,它将破坏表。
  {write_concurrency,boolean()}
  性能调优。默认为false,在这种情况下,一个对表进行改变(写入)的操作将获得独占访问,阻塞同一表的任何并发访问,直到完成为止。如果设置为true,该表针对并发写访问进行了优化。同一表的不同对象可以通过并发进程进行改变(和读取)。这在一定程度上是以牺牲内存消耗和顺序访问和并发读取的性能为代价的。write_concurrency选项可以与write_concurrency选项相结合。通常,当大型并发读爆发和大型并发写爆发是常见的(请参阅write_concurrency选项的文档以获得更多信息),您通常希望将它们组合在一起。注意,这个选项不会改变任何关于原子性和隔离性的保证。在多个对象(如insert/2)中做出这样的承诺的函数将从这个选项中获得更少(或没有)。
在当前的实现中,表类型ordered_set不受此选项的影响。此外,write并发性和read并发性所造成的内存消耗是每个表的一个常量开销。当两个选项组合在一起时,这个开销可能会特别大。
  {read_concurrency,boolean()}
  性能调优。默认是false。当设置为true时,该表针对并发读操作进行了优化。当在带有SMP支持的运行时系统上启用此选项时,读取操作将变得更加便宜;特别是在有多个物理处理器的系统上。然而,在读写操作之间切换变得更加昂贵。当并发读操作比写操作要频繁得多,或者当并发读和写出现大的读和写时,您通常希望启用这个选项。很多读操作都不会被写,很多的写操作都不会被读中断。当常见的访问模式是一些读操作,并重复使用一些写操作时,您通常不希望启用这个选项。在这种情况下,启用这个选项获得性能下降。read_concurrency选项可以与write_concurrency选项相结合。通常,当大型并发读爆发和大型并发写爆发是常见的时候,您通常需要将它们组合在一起。
  compressed
  如果存在这个选项,那么表数据将以更紧凑的格式存储,以减少内存。缺点是它会使表操作变慢。特别是需要检查整个对象的操作,例如match和select,将会变得更慢。在当前的实现中,关键元素没有被压缩。

next(Tab, Key1) -> Key2 | '$end_of_table'
  Types:
    Tab = tab()
    Key1 = Key2 = term()
  返回下一个键Key2,按照表Tab中的键Key1。如果该表是ordered_set类型,那么Erlang项顺序中下一个键就会返回。如果表是任何其他类型的表,那么根据表的内部顺序返回下一个键。如果没有下一个键,就会返回“$end_of_table”。
  使用first/1来找到表格中的第一个键。
  除非使用safe_fixtable/2来保护一组类型set、bag或duplicate_bag的表,否则,如果对表进行并发更新,则遍历可能会失败。如果该表是ordered_set的,那么函数就会按顺序返回下一个键,即使对象已经不存在了。

prev(Tab, Key1) -> Key2 | '$end_of_table'
  Types:
    Tab = tab()
    Key1 = Key2 = term()
  按照ordered_set类型的表Tab中的Erlang项顺序返回前面的键Key2。如果表是任何其他类型的,那么该函数就是next/2的同义词。如果没有以前的键,则返回“$end_of_table”。
  使用last/1来找到表中的最后一个键。

rename(Tab, Name) -> Name
  Types:
    Tab = tab()
    Name = atom()
  将命名的表Tab重新命名为新名称。之后,旧的名称不能用于访问表。重命名一个未命名的表没有效果。

repair_continuation(Continuation, MatchSpec) -> Continuation
  Types:
    Continuation = continuation()
    MatchSpec = match_spec()
  这个函数可以用来恢复ets:select/3或ets:select/1的不透明延续,如果延续已经通过了外部项格式(在节点之间发送或存储在磁盘上)。
  这个函数的原因是延续项包含了编译的match_spec,因此如果转换为外部项格式,将会失效。考虑到原始的match_spec是完整的,延续可以被恢复,这意味着它可以再次被用于后续的ets:select/1调用,即使它已经存储在磁盘上或另一个节点上。
  作为一个例子,以下的调用序列将失败:
T=ets:new(x,[]),
...
{_,C} = ets:select(T,ets:fun2ms(fun({N,_}=A)
when (N rem 10) =:= 0 -> A end),10),
Broken = binary_to_term(term_to_binary(C)),
ets:select(Broken).
…下面的顺序将会起作用:
T=ets:new(x,[]),
...
MS = ets:fun2ms(fun({N,_}=A)
when (N rem 10) =:= 0 -> A end),
{_,C} = ets:select(T,MS,10),
Broken = binary_to_term(term_to_binary(C)),
ets:select(ets:repair_continuation(Broken,MS)).
…正如对ets:repair_continuation/2的调用将重新建立(谨慎地)失效的延续。
注意:
  在应用程序代码中很少需要这个函数。它被Mnesia用于实现分布式select/3和select/1序列。一个普通的应用程序要么使用Mnesia,要么保持延续不被转换为外部格式。
  没有一个编译的matchspec的外部表示的原因是性能。它可能会在将来的版本中发生变化,而这个接口将保留向后兼容性。

safe_fixtable(Tab, Fix) -> true
  Types:
    Tab = tab()
    Fix = boolean()
  锁定一个类型是 set,bag 或 duplicate_bag 的表,使其可以安全遍历表里的数据。
  一个进程通过调用safe_fixtable(Tab, true)来锁定一个表。该表保持不变,直到进程通过调用safe_fixtable(Tab, false)释放它,或者直到进程终止为止。
  如果同时有几个进程锁定一个表,那么表会一直保持锁定状态,直到所有进程都释放它(或崩溃)。有一个引用计数器记录着每个进程的操作,有 N 个持续的锁定操作必须有 N 个释放操作,表才会真正被释放。
  当一个表被锁定,一序列的ets:first/1和ets:next/2的调用都会保证成功执行,并且表里的每一个对象数据只返回一次,即使在遍历的过程中,对象数据被删除或插入。在遍历过程中插入到表里的新数据可能由 ets:next/2 返回(这取决有键的内部顺序)。
clean_all_with_value(Tab,X) ->
safe_fixtable(Tab,true),
clean_all_with_value(Tab,X,ets:first(Tab)),
safe_fixtable(Tab,false).

clean_all_with_value(Tab,X,'$end_of_table') ->
true;
clean_all_with_value(Tab,X,Key) ->
case ets:lookup(Tab,Key) of
[{Key,X}] ->
ets:delete(Tab,Key);
_ ->
true
end,
clean_all_with_value(Tab,X,ets:next(Tab,Key)).
  一个被锁定的表是不会有被删除的对象数据从表里被实际删除,直到它被释放。如果一个进程锁定一个表,并不释放它,那些已删除的对象数据所占用的内存将永远不会得到释放。对表操作的性能也会显著降低。
  可以使用 ets:info/2 来查看有哪些进程锁定了表。有很多进程锁定表的系统需要一个监控来给那些锁定表很长时间的进程发送警告消息。
  对于 ordered_set 类型的表,ets:safe_fixtable/2 是没必要的,因为 ets:first/1 和 ets:next/2 将总会调用成功。

select(Tab, MatchSpec) -> [Match]
  Types:
    Tab = tab()
    MatchSpec = match_spec()
    Match = term()
  使用一个匹配描述从表 Tab 里匹配对象。此函数调用比 ets:match/2 和 ets:match_object/2 更常用。以下是一些最简单形式的匹配描述:
MatchSpec = [MatchFunction]
MatchFunction = {MatchHead, [Guard], [Result]}
MatchHead = "Pattern as in ets:match"
Guard = {"Guardtest name", ...}
Result = "Term construct"
  这意味着匹配描述总是含有一个以上元组元素的列表(含有三个参数的元组元素),元组的第一个元素应是 ets:match/2 的文档中所描述的模式,第二个元素应是含 0 个或多个断言测试的列表,第三个元素应是包含关于实际返回值的描述的列表,通常是一个对返回值全描述的列表,即返回匹配对象的所有项目。
  返回值的结构使用 MatchHead 所绑定的 "match variables",或者使用特殊的匹配值 '$_'(整个匹配对象)和 '$$'(包含所有匹配值的列表)。

ets:match(Tab,{'$1','$2','$3'}) 等同于 ets:select(Tab,[{{'$1','$2','$3'},[],['$$']}])
ets:match_object(Tab,{'$1','$2','$1'}) 等同于 ets:select(Tab,[{{'$1','$2','$1'},[],['$_']}])
复合术语可以通过简单地编写一个列表来构造结果部分,因此下面的代码:
ets:select(Tab,[{{'$1','$2','$3'},[],['$$']}]) 等同于 ets:select(Tab,[{{'$1','$2','$3'},[],[['$1','$2','$3']]}])
在匹配头中所有绑定的变量作为一个列表。如果要构造元组,就必须在元组中编写一个元组,其中的单个元素是tuple想要构造的元组(因为普通的元组可能被误认为是一个守卫)。因此下面的调用:
ets:select(Tab,[{{'$1','$2','$1'},[],['$_']}]) 等同于 ets:select(Tab,[{{'$1','$2','$1'},[],[{{'$1','$2','$3'}}]}])
这种语法相当于跟踪模式中使用的语法(参见see dbg(3))
这些警卫被构造成元组,其中第一个元素是测试的名称,其余的元素是测试的参数。要检查绑定到匹配变量$1的元素的特定类型(比如列表),您可以将测试写成{is_list,'$1'}。如果测试失败,表中的对象将无法匹配,并且将尝试下一个MatchFunction(如果有的话)。可以使用Erlang中的大多数警卫测试,但是只有新版本的前缀is_是允许的(如is_float、is_atom等)。
警卫部分还可以包含逻辑和算术运算,它们使用与警卫测试(前缀表示法)相同的语法编写,因此在Erlang中编写的警卫测试是这样的:
is_integer(X), is_integer(Y), X + Y < 4711
像这样表示(X换成'$1'和Y换成'$2'):
[{is_integer, '$1'}, {is_integer, '$2'}, {'<', {'+', '$1', '$2'}, 4711}]
  在ordered_set类型的表格中,对象的访问顺序与first/next遍历的顺序相同。这意味着匹配规范将在first/next顺序中对带有键的物体执行,相应的结果列表将按照该执行的顺序执行。

select(Tab, MatchSpec, Limit) -> {[Match], Continuation} | '$end_of_table'
  Types:
    Tab = tab()
    MatchSpec = match_spec()
    Limit = integer() >= 1
    Match = term()
    Continuation = continuation()
  用法跟 ets:select/2 相似,但只返回限定数量(Limit)的匹配对象数据。Continuation 项可在后续的 ets:select/1 调用中获取下一组的匹配对象数据。在操作表中对象数据时,这是一种很有效的操作方式,并且比通过调用 ets:first/1 和 ets:next/1 逐个访问对象还要快。
  如果表是空的,则返回$end_of_table。

select(Continuation) -> {[Match], Continuation} | '$end_of_table'
  Types:
    Match = term()
    Continuation = continuation()
  继续从 ets:select/3 开始的匹配。 下一次匹配到的限定数量 Limit(Limit 由 ets:select/3 初始所得)的对象数据将与新的 Continuation 一起返回,新的 Continuation 将在后续调用该函数时被使用。
  如果表中已没有剩余的对象,则返回 '$end_of_table'。

select_count(Tab, MatchSpec) -> NumMatched
  Types:
    Tab = tab()
    MatchSpec = match_spec()
    NumMatched = integer() >= 0
  使用match_spec将表Tab中的对象与之匹配。如果匹配的对象返回true的话,该对象就会被认为是匹配并被计算在内。对于任何来自match_spec的结果,对象不被认为是匹配的,因此不被计算在内。
  这个函数可以被描述为一个matchd_elete/2,它实际上并不删除任何元素,但只计算它们。
  这个函数返回匹配的对象的数量。

select_delete(Tab, MatchSpec) -> NumDeleted
  Types:
    Tab = tab()
    MatchSpec = match_spec()
    NumDeleted = integer() >= 0
  使用一个匹配规范 MatchSpec 来匹配表 Tab 里的对象数据。如果对一条对象数据使用匹配规范时返回 true,并且该条对象数据会从表里移除。对于任何被匹配规范(match_spec)所匹配到的对象数据会被作为返回值而保留下来。该函数比 ets:match_delete/2 还更通用(ets:match_delete/2 的代码实现里还调用到 ets:select_delete/2 方法)。
  函数最后会返回一个实际在表里被删除的对象的数量值。

select_reverse(Tab, MatchSpec) -> [Match]
  Types:
    Tab = tab()
    MatchSpec = match_spec()
    Match = term()
  像select/2这样的工作,但是对于ordered_set表类型返回列表的顺序是相反的。对于所有其他表类型,返回值与select/2的返回值相同。

select_reverse(Tab, MatchSpec, Limit) -> {[Match], Continuation} | '$end_of_table'
  Types:
    Tab = tab()
    MatchSpec = match_spec()
    Limit = integer() >= 1
    Match = term()
    Continuation = continuation()
  像select/3这样的工作,但是对于ordered_set表类型,遍历是在Erlang项顺序的最后一个对象上开始的,并向第一个方向移动。对于所有其他表类型,返回值与select/3相同。
  请注意,这并不等同于反转select/3调用的结果列表,因为结果列表不仅颠倒了,而且还包含了表中最后的Limit匹配对象,而不是第一个。

select_reverse(Continuation) -> {[Match], Continuation} | '$end_of_table'
  Types:
    Continuation = continuation()
    Match = term()
  继续从ets:select_reverse/3开始一个匹配。如果是ordered_set类型的表,表的遍历将继续从Erlang数据里最早的键的对象数据开始。返回的列表也将包含跟键按相反的顺序的对象数据。
  如果是其他类型的的表,那么该函数的效用跟 ets:select/1 一样。
例子:
1> T = ets:new(x,[ordered_set]).
2> [ ets:insert(T,{N}) || N <- lists:seq(1,10) ].
...
3> {R0,C0} = ets:select_reverse(T,[{'_',[],['$_']}],4).
...
4> R0.
[{10},{9},{8},{7}]
5> {R1,C1} = ets:select_reverse(C0).
...
6> R1.
[{6},{5},{4},{3}]
7> {R2,C2} = ets:select_reverse(C1).
...
8> R2.
[{2},{1}]
9> '$end_of_table' = ets:select_reverse(C2).
...

setopts(Tab, Opts) -> true
  Types:
    Tab = tab()
    Opts = Opt | [Opt]
    Opt = {heir, pid(), HeirData} | {heir, none}
    HeirData = term()
  设置表选项。目前唯一允许在表被创建之后设置的选项是heir。调用过程必须是表所有者。

slot(Tab, I) -> [Object] | '$end_of_table'
  Types:
    Tab = tab()
    I = integer() >= 0
    Object = tuple()
  这个函数主要用于调试,通常应该使用first/next或last/prev。
  返回表Tab的I:th槽中的所有对象。一个表可以通过重复调用该函数来遍历,从第一个槽I=0开始,并在返回“$end_of_table”时结束。如果I参数超出了范围,那么这个函数就会失败。
  除非使用safe_fixtable/2来保护类型set、bag或duplicate_bag的表,否则,如果对表进行并发更新,则遍历可能会失败。如果该表是ordered_set的,那么该函数将返回包含Erlang项顺序中的I:th对象的列表。

tab2file(Tab, Filename) -> ok | {error, Reason}
  Types:
    Tab = tab()
    Filename = file:name()
    Reason = term()
  将表Tab转储到文件Filename。
  相当于tab2file(Tab, Filename,[])。

tab2file(Tab, Filename, Options) -> ok | {error, Reason}=
  Types:
    Tab = tab()
    Filename = file:name()
    Options = [Option]
    Option = {extended_info, [ExtInfo]}
    ExtInfo = md5sum | object_count
    Reason = term()
  将表Tab转储到文件Filename。
  当转储表时,关于该表的某些信息会被转储到转储文件开头的头部。这些信息包含关于表类型、名称、保护、大小、版本的数据,如果它是一个命名表。它还包含关于扩展信息被添加到文件中的注释,它可以是文件中对象的计数,也可以是文件头和记录的MD5和。
  如果表是public,并且在转储期间从表中添加或删除记录,那么标题中的size字段可能与文件中的实际记录数不一致。在转储期间更新的public表,并且在读取时需要验证,至少需要一个扩展信息的字段,以便以后的读取验证进程是可靠的。
  extended_info选项指定将哪些额外信息写入表转储:
  object_count
    实际上写入文件的对象的数量在文件页脚中被记录,为什么即使文件在转储期间被更新,也可以验证文件截断。
  md5sum
    文件中的头和对象是使用内置的MD5函数来校验的。所有对象的MD5和都写在文件页脚上,这样在读取时验证将检测到文件数据中最轻微的位翻转。使用这种方法需要花费相当多的CPU时间。
  当使用extended_info选项时,它会导致在stdlib-1.15.1版本之前的ets版本中无法读取的文件。

tab2list(Tab) -> [Object]
  Types:
    Tab = tab()
    Object = tuple()
  返回表Tab中所有对象的列表。

tabfile_info(Filename) -> {ok, TableInfo} | {error, Reason}
  Types:
    Filename = file:name()
    TableInfo = [InfoItem]
    InfoItem = {name, atom()}
      | {type, Type}
      | {protection, Protection}
      | {named_table, boolean()}
      | {keypos, integer() >= 0}
      | {size, integer() >= 0}
      | {extended_info, [ExtInfo]}
      | {version,
      {Major :: integer() >= 0,
      Minor :: integer() >= 0}}
    ExtInfo = md5sum | object_count
    Type = bag | duplicate_bag | ordered_set | set
    Protection = private | protected | public
    Reason = term()
  返回通过tab2file/2或tab2file3导出到文件的表的信息。
  以下项目将返回:
  name
    导出表的名字。如果表是一个命名表,那么当表格使用file2tab/2从文件中加载时,就不可能存在同名的表。如果表没有被保存为指定的表,那么当从文件加载表格时,这个字段根本没有任何意义。
  type
    ets导出表的类型(即set、bag、duplicate_bag或ordered_set)。这种类型将在再次载入表时使用。
  protection
    导出表的保护类型(即private, protected 或 public)。从文件中加载的表将得到同样的保护。
  named_table
    true如果在转储文件时该表是一个命名表,否则false。请注意,当一个具名表从一个文件中加载时,系统中不能存在同名的表。
  keypos
    将表的keypos转储到文件中,这将在再次加载表格时使用。
  size
    当表转储到文件时,表中的对象的数量,在公共表的情况下,不需要对应实际保存到文件的对象的数量,因为在表转储期间,对象可能已经被另一个进程添加或删除了。
  extended_info
    在文件页脚中写入的扩展信息,允许在表格加载过程中进行更强的验证,如指定为tab2file/3。注意,这个函数只告诉我们哪些信息是存在的,而不是文件页脚中的值。该值是一个包含一个或多个原子object_count和md5sum的列表。
  version
    {Major,Minor}包含了ets表转储文件格式的主要和次要版本。这个版本字段是由stdlib-1.5.1开始添加的,旧版本的文件在这个字段中会返回{0,0}。

  如果文件不可访问,严重损坏,或者没有使用tab2file/2或tab2file/3生成的文件,则返回错误。

table(Tab) -> QueryHandle
table(Tab, Options) -> QueryHandle
  Types:
    Tab = tab()
    QueryHandle = qlc:query_handle()
    Options = [Option] | Option
    Option = {n_objects, NObjects} | {traverse, TraverseMethod}
    NObjects = default | integer() >= 1
    TraverseMethod = first_next
      | last_prev
      | select
      | {select, MatchSpec :: match_spec()}
  返回QLC(Query List Comprehension)查询句柄。模块QLC实现了一种主要针对Mnesia的查询语言,但是ETS表、Dets表和列表也被QLC识别为数据的来源。调用ETS:table/1,2是使ETS表Tab可用到QLC的方法。
  当在关键位置上只有简单的限制时,QLC使用ets:lookup/2查找键,但如果不可能,整个表就会被遍历。选项遍历决定了这是如何完成的:
  first_next.
    表被遍历一次一个键通过调用ets:first/1和ets:next/2。
  last_prev.
    表被遍历一次一个键通过调用ets:last/1和ets:prev/2。
  select.
    通过调用ets:select/3和ets:select/1来遍历表。选项n_object决定返回的对象的数量(select/3的第三个参数);默认情况下,一次返回100个对象。match_spec(select/3的第二个参数)由QLC组装:简单的过滤器被转换成等价的匹配规范,而更复杂的过滤器必须应用于select/3返回的所有对象,给定匹配所有对象的match_spec。
  {select, MatchSpec}.
    至于select表,调用ets:select/3和ets:select/1遍历。不同之处在于,match_spec是显式的。这就是如何在QLC提供的语法中声明不容易表达的匹配规范。
下面的例子使用一个显式的match_spec来遍历表:
9> true = ets:insert(Tab = ets:new(t, []), [{1,a},{2,b},{3,c},{4,d}]),
MS = ets:fun2ms(fun({X,Y}) when (X > 1) or (X < 5) -> {Y} end),
QH1 = ets:table(Tab, [{traverse, {select, MS}}]).
一个带有隐式match_spec的例子:
10> QH2 = qlc:q([{Y} || {X,Y} <- ets:table(Tab), (X > 1) or (X < 5)]).
后一个例子实际上相当于前者,可以通过函数qlc:info/1来验证。
11> qlc:info(QH1) =:= qlc:info(QH2).
true
  qlc:info/1 返回关于查询句柄的信息,在这种情况下,两个查询句柄返回相同的信息。

test_ms(Tuple, MatchSpec) -> {ok, Result} | {error, Errors}
  Types:
    Tuple = tuple()
    MatchSpec = match_spec()
    Result = term()
    Errors = [{warning | error, string()}]
  这个函数是用来测试在调用ets:select/2时使用的match_spec的一个实用程序。这个函数都测试了匹配规范的“语法”正确性,并对对象元组运行Match_Spec。如果匹配规范包含错误,那么元组{error, Errors} 就会返回,Errors是一个自然语言描述的列表,描述了match_spec的错误。如果match_spec在语法上是正确的,那么函数就会返回{ok,Result} ,Result是在真实ets:select/2调用结果:如果匹配规范不匹配对象元组,则返回false。
  这是一个有用的调试和测试工具,特别是在编写复杂的ets:select/2调用时。

to_dets(Tab, DetsTab) -> DetsTab
  Types:
    Tab = tab()
    DetsTab = dets:tab_name()
  在已经打开的ETS表格中,填满已经创建的/打开的Dets表。在插入对象之前,Dets表被清空。

update_counter(Tab, Key, UpdateOp) -> Result
update_counter(Tab, Key, UpdateOp :: [UpdateOp]) -> [Result]
update_counter(Tab, Key, Incr) -> Result
  Types:
    Tab = tab()
    Key = term()
    UpdateOp = {Pos, Incr} | {Pos, Incr, Threshold, SetValue}
    Pos = Incr = Threshold = SetValue = Result = integer()
  这个函数提供了一种有效的方法来更新一个或多个计数器,而不必费力地查找一个对象,通过增加一个元素来更新对象,并再次将产生的对象插入到表中。(更新是用原子的方式完成的;也就是说,在操作过程中,没有进程可以访问ets表。)
  它将破坏性地更新表Tab的键Key的对象,增加Incr到Pos:th位置的元素。新的计数器值返回。如果没有指定位置,则会更新下一个键(keypos+1)的元素。
  如果指定了阈值,则计数器将被重置为值SetValue,如果下列条件发生:
  Incr不是负数(>=0),结果将大于(>)Threshold;
  Incr是负的(<0),结果将小于(<)Threshold。
  可以提供UpdateOp的列表,以便在对象中执行几个更新操作。操作按照列表中指定的顺序执行。如果相同的计数器位置在列表中不止一次出现,那么相应的计数器将会被更新几次,每次都是基于前面的结果。返回值是每个更新操作的新计数器值的列表,其顺序与操作列表中的顺序相同。如果指定了空链表,则不会更新任何东西,并返回一个空列表。如果函数失败,则根本不需要更新。
  给定的键用来识别对象,通过匹配一个集合表中的对象的键,或者与ordered_set表中的物体的键进行比较(参见lookup/2和new/2,了解差异的详细信息)。
  这个函数将会因为badarg而失败,如果:
    这个表不是类型set或ordered_set;
    没有正确的键存在;
    对象的参数是错的;
    更新的元素不是整数;
    更新的元素也是键,或者任何Pos,Incr,阈值或SetValue都不是整数。

update_element(Tab, Key, ElementSpec :: {Pos, Value}) -> boolean()
update_element(Tab, Key, ElementSpec :: [{Pos, Value}]) -> boolean()
  Types:
    Tab = tab()
    Key = term()
    Value = term()
    Pos = integer() >= 1
  这个函数提供了一种有效的方法来更新对象中的一个或多个元素,而不必查找、更新和回写整个对象。
  该函数把 ETS 表 Tab 里键为 Key 的对象数据的第 Pos 个元素数据的值更改为 Value。
  一个{Pos,Value}列表可以更新同一个对象中的几个元素。如果相同的位置在列表中出现不止一个,那么列表中的最后一个值将被写入。如果列表是空的,或者函数失败,则根本不会进行更新。这个函数也是原子的,因为其他进程永远看不到任何中间结果。
  如果找到了带有键Key的对象,则该函数返回true;反之则返回false。
  给定的键用来识别对象,通过匹配一个set表中的对象的键,或者与ordered_set表中的对象的键进行比较(参见lookup/2和new/2,了解差异的详细信息)。
  这个函数将会因为badarg而失败,如果:
    这个表不是类型set或ordered_set,
    Pos小于1或大于1,或者,
    更新的元素也是键。