非常教程

Erlang 20参考手册

stdlib

qlc

Module

qlc

模块摘要

查询Mnesia接口,ETS,Dets等。

描述

该模块提供了一种查询接口MnesiaETSDets,和其它数据提供对象的迭代式遍历结构。

概观

该模块为QLC表提供查询接口。典型的QLC表格是Mnesia,ETS和Dets表格。还为用户定义的表提供支持,请参见部分Implementing a QLC Table。一个查询使用表示查询列表综合(QLCs)。查询的答案由满足查询的QLC所表达的约束的QLC表中的数据确定。如描述QLCs类似于普通列表解析Erlang Reference ManualProgramming Examples,所不同的是在图案引入变量不能在列表中的表达式中使用。在不存在的优化和选项,如的cacheunique(参见Common Options,没有QLC表格的每个QLC都会评估相同的普通列表理解答案列表。

当普通的列表解析评估为列表时,调用q/1,2返回一个查询句柄。要获得查询的所有答案,eval/1,2将以查询句柄作为第一个参数进行调用。查询句柄本质上是在模块调用中创建的功能对象(funs)q/1,2。由于funs涉及模块代码,因此如果要更换模块代码,请小心不要让查询句柄太长。代码替换Compilation and Code Loading在Erlang参考手册的章节中有描述。答案列表也可以通过使用查询光标在区块中遍历。查询游标是通过调用创建的cursor/1,2以查询句柄作为第一个参数。查询游标本质上是Erlang进程。一次一个答案从查询游标进程发送到创建游标的进程。

句法

语法上的QLC与普通的列表解析具有相同的部分:

[Expression || Qualifier1, Qualifier2, ...]

Expression模板)是任何Erlang表达式。限定符是过滤器发生器。过滤器是返回的Erlang表达式boolean()。生成器具有表单Pattern <- ListExpression,其中ListExpression是对表达式求值的查询句柄或列表。查询扶手由返回append/1,2keysort/2,3q/1,2sort/1,2string_to_handle/1,2,3,和table/2

评估

查询句柄按以下顺序评估:

  • 检查选项和收集有关表格的信息。结果,限定符在优化阶段被修改。
  • 所有列表表达式都被评估。如果创建了游标,则会在游标进程中进行评估。对于QLC的列表表达式,QLC的生成器的列表表达式也被评估。如果列表表达式具有副作用,请小心,因为列表表达式按未指定的顺序进行评估。
  • 答案可以通过从左到右评估限定符,当某个过滤器返回时回溯false,或者当所有过滤器返回时收集模板来找到答案true

不返回boolean()但失败的过滤器根据其语法进行不同的处理:如果过滤器是警卫,则返回false,否则查询评估失败。这种行为使得qlc模块可以做一些优化而不影响查询的含义。例如,当测试某个表的位置和一个或多个常数的相等性时,只有具有相同值的对象才是进一步评估的候选对象。其他对象保证使过滤器返回false,但从未失败。(小)候选对象集通常可以通过查找表的一些关键值或使用匹配规范遍历表找到。有必要在表生成器之后立即放置保护过滤器,否则候选对象不会被限制在一个小集合中。原因是可能导致查询评估失败的对象不能通过查找关键字或运行匹配规范来排除。

加入

qlc模块支持两个查询句柄的快速连接。如果测试P1一个查询处理程序的某个位置P2和另一个查询处理程序的某个位置是否相等,则可以快速连接。提供了两种快速连接方法:

  • 查找加入穿越一个查询手柄的所有对象,并找到这样的其他手柄的物体(QLC表),在价值观P1P2匹配或比较平等的。该qlc模块不会创建任何索引,而是使用QLC表的键位置和索引位置查找值。
  • 合并连接根据需要对每个查询句柄的对象进行排序,并过滤出值P1P2不相等的值的对象。如果存在许多具有相同值的对象,P2则会为等价类使用临时文件。

qlc模块警告在编译时如果QLC查询相结合这样一个以上的加入有可能处理。也就是说,没有提供可以在可能的连接操作之间选择好顺序的查询计划器。用户可以通过引入查询句柄来订购连接。

连接将被表示为一个保护过滤器。过滤器必须放置在两个连接的发生器之后,可能在使用来自其他发生器但除了两个连接的发生器之外的变量的防护过滤器之后。该qlc模块检查的操作数中=:=/2==/2is_record/2element/2,和逻辑运算符(and/2or/2andalso/2orelse/2xor/2)确定它连接时要考虑的。

常用选项

下列选项被接受cursor/2eval/2fold/4,和info/2

  • {cache_all, Cache},除了表和列表之外,其中Cache等于etslist{cache, Cache}选项添加到查询的每个列表表达式中。默认为{cache_all, no}。选项cache_all相当于{cache_all, ets}
  • {max_list_size, MaxListSize},其中MaxListSize是外部格式的字节大小。如果收集到的对象的累积大小超过MaxListSize,对象将被写入临时文件。该选项由选项{cache, list}和合并连接方法使用。默认为512 * 1024字节。
  • {tmpdir_usage, TmpFileUsage}确定当qlc即将在选项设置的目录上创建临时文件时采取的操作tmpdir。如果值是not_allowed,则返回错误元组,否则会根据需要创建临时文件。默认值是allowed,这意味着不会采取进一步的行动。值info_msgwarning_msg并且error_msg意味着在模块中具有相应名称的函数error_logger被调用来打印一些信息(当前是堆栈跟踪)。
  • {tmpdir, TempDirectory}设置合并连接用于临时文件的目录和选项{cache, list}。该选项还覆盖选项tmpdirkeysort/3sort/2。默认为"",这意味着file:get_cwd()使用返回的目录。
  • {unique_all, true}{unique, true}向查询的每个列表表达式添加一个选项。默认为{unique_all, false}。选项unique_all相当于{unique_all, true}

入门

如前所述,查询以列表理解语法表示,如ExpressionsErlang参考手册中所述。在下文中,假定对列表解读有一定的熟悉度。在节中的示例List Comprehensions中编程实例可以让你开始。注意,列表解析不会为语言增加任何计算能力;任何可以通过列表解析完成的事情也可以在没有它们的情况下完成。但是他们添加了表达简单搜索问题的语法,一旦您习惯了它,它就是紧凑而清晰的。

许多列表理解表达式可以由qlc模块评估。例外情况是表达式,以便在列表理解中稍后在某些生成器中使用模式(或过滤器)中引入的变量。作为一个例子,考虑下面的一个实现lists:append(L)[X ||Y <- L, X <- Y]Y在第一台发电机中引入,并在第二台发电机中使用。当可以选择使用哪个列表时,普通列表理解通常是优选的。一个不同之处在于eval/1,2收集的答案在最终被颠倒的列表中,而列表解析在收集到的堆栈上收集到最终解除的答案。

qlc模块主要添加到列表解析中的是,可以从小块中的QLC表中读取数据。QLC表是通过调用创建的qlc:table/2。通常qlc:table/2不是直接从查询中调用,而是通过一些数据结构的接口函数。二郎/ OTP包括这样的功能的几个例子:mnesia:table/1,2ets:table/1,2,和dets:table/1,2。对于给定的数据结构,许多函数都可以创建QLC表,但这些函数的通用之处在于它们返回由创建的查询句柄qlc:table/2。使用Erlang / OTP提供的QLC表通常可能就足够了,但更高级的用户部分Implementing a QLC Table描述了函数调用的实现qlc:table/2

此外qlc:table/2,其他函数返回查询句柄。它们比表格更少使用,但有时也很有用。qlc:append/1,2从多个表或列表中遍历对象。例如,如果您想遍历查询的所有答案QH,然后完成一个字词{finished},则可以通过调用来完成qlc:append(QH, [{finished}])append/2首先返回所有的对象QH,然后{finished}。如果{finished}在答案中存在一个元组QH,它会从中返回两次append/2

作为另一个例子,考虑将两个查询的答案连接起来QH1QH2同时删除所有重复项。这是通过使用选项来完成的unique

qlc:q([X || X <- qlc:append(QH1, QH2)], {unique, true})

成本很高:每个返回的答案都存储在ETS表中。在返回答案之前,在ETS表中查找它是否已经返回。如果没有这个unique选项,所有的答案QH1都会被返回,然后是所有的答案QH2。该unique选项保留剩余答案之间的顺序。

如果答案的顺序不重要,则可以选择其他unique选项,即对答案进行唯一排序:

qlc:sort(qlc:q([X || X <- qlc:append(QH1, QH2)], {unique, true})).

此查询也删除重复项,但答案是排序的。如果答案很多,则使用临时文件。请注意,要获得第一个唯一答案,必须找到并排序所有答案。两种方法都可以通过比较答案来找到重复的答案,也就是说,如果A1并且A2按照该顺序找到了答案,那么A2就是删除了答案A1 == A2

为了只返回几个答案,可以使用游标。以下代码返回不超过5个使用ETS表格存储唯一答案的答案:

C = qlc:cursor(qlc:q([X || X <- qlc:append(QH1, QH2)],{unique,true})),
R = qlc:next_answers(C, 5),
ok = qlc:delete_cursor(C),
R.

QLC方便地陈述来自两个或更多表格的数据的限制。以下示例在位置2上的两个查询句柄上进行自然连接:

qlc:q([{X1,X2,X3,Y1} || 
          {X1,X2,X3} <- QH1, 
          {Y1,Y2} <- QH2, 
          X2 =:= Y2])

qlc模块此不同,这取决于查询把手评估QH1QH2。例如,如果X2与QLC表的键匹配,则查找连接方法将在查找表中的键值时遍历对象QH2。然而,如果没有X2Y2抵靠键或QLC表的索引位置相匹配时,合并连接方法可以确保QH1QH2均排序上的位置2和下一个做通过遍历对象逐一加入。

Option join可以用来强制qlc模块使用某种连接方法。对于本节的其余部分,假设已经选择了称为“嵌套循环”的过慢连接方法:

qlc:q([{X1,X2,X3,Y1} || 
          {X1,X2,X3} <- QH1, 
          {Y1,Y2} <- QH2, 
          X2 =:= Y2],
      {join, nested_loop})

在这种情况下,过滤器将应用于每个可能的答案对,QH1并且QH2一次一个答案。如果存在M个答案QH1和N个答案QH2,则过滤器运行M * N次。

如果QH2gb_trees按照章节中的定义调用函数Implementing a QLC Table,那么gb_table:table/1为每个答案启动gb-tree的迭代器QH1。gb-tree的对象然后被一个接一个地返回。在这种情况下,这可能是遍历表的最有效方式,因为它需要最小的计算能力来获得以下对象。但是,如果QH2不是表格而是更复杂的QLC,使用一些RAM内存来收集缓存中的答案会更有效率,特别是如果只有几个答案的话。然后必须假定评估QH2没有副作用,因此如果QH2仅评估一次,则查询的含义不会改变。缓存答案的一种方法是评估QH2首先替换QH2查询中的答案列表。另一种方法是使用选项cache。它表达如下:

QH2' = qlc:q([X || X <- QH2], {cache, ets})

或只有

QH2' = qlc:q([X || X <- QH2], cache)

选项的作用cache是当发电机QH2'第一次运行时,每个答案都存储在ETS表中。当下一个答案QH1被尝试时,答案QH2'将从ETS表中复制而来,非常快。至于选项unique,成本是可能相当大量的RAM存储器。

选项{cache, list}提供了将答案存储在过程堆列表中的可能性。这有可能比ETS表格更快,因为不需要从表格中复制答案。但是,由于堆的垃圾收集量更大,并且由于堆更大,因此会增加RAM的内存消耗,因此通常会导致评估速度变慢。缓存列表的另一个缺点是,如果列表大小超过限制,则会使用临时文件。从文件中读取答案要比从ETS表复制要慢得多。但是如果可用的RAM内存很少,那么将其设置limit为较低的值是另一种选择。

选项cache_all可以设置为etslist评估查询时。它向查询的所有级别上的QLC表和列表除外添加cache{cache, list}选择每个列表表达式。这可以用于测试缓存是否会提高效率。如果答案是肯定的,则需要进一步的测试来确定要被缓存的发电机。

实施QLC表

As an example of how to use function table/2, the implementation of a QLC table for the gb_trees module is given:

-module(gb_table).

-export([table/1]).

table(T) ->
    TF = fun() -> qlc_next(gb_trees:next(gb_trees:iterator(T))) end,
    InfoFun = fun(num_of_objects) -> gb_trees:size(T);
                 (keypos) -> 1;
                 (is_sorted_key) -> true;
                 (is_unique_objects) -> true;
                 (_) -> undefined
              end,
    LookupFun =
        fun(1, Ks) ->
                lists:flatmap(fun(K) ->
                                      case gb_trees:lookup(K, T) of
                                          {value, V} -> [{K,V}];
                                          none -> []
                                      end
                              end, Ks)
        end,
    FormatFun =
        fun({all, NElements, ElementFun}) ->
                ValsS = io_lib:format("gb_trees:from_orddict(~w)",
                                      [gb_nodes(T, NElements, ElementFun)]),
                io_lib:format("gb_table:table(~s)", [ValsS]);
           ({lookup, 1, KeyValues, _NElements, ElementFun}) ->
                ValsS = io_lib:format("gb_trees:from_orddict(~w)",
                                      [gb_nodes(T, infinity, ElementFun)]),
                io_lib:format("lists:flatmap(fun(K) -> "
                              "case gb_trees:lookup(K, ~s) of "
                              "{value, V} -> [{K,V}];none -> [] end "
                              "end, ~w)",
                              [ValsS, [ElementFun(KV) || KV <- KeyValues]])
        end,
    qlc:table(TF, [{info_fun, InfoFun}, {format_fun, FormatFun},
                   {lookup_fun, LookupFun},{key_equality,'=='}]).

qlc_next({X, V, S}) ->
    [{X,V} | fun() -> qlc_next(gb_trees:next(S)) end];
qlc_next(none) ->
    [].

gb_nodes(T, infinity, ElementFun) ->
    gb_nodes(T, -1, ElementFun);
gb_nodes(T, NElements, ElementFun) ->
    gb_iter(gb_trees:iterator(T), NElements, ElementFun).

gb_iter(_I, 0, _EFun) ->
    '...';
gb_iter(I0, N, EFun) ->
    case gb_trees:next(I0) of
        {X, V, I} ->
            [EFun({X,V}) | gb_iter(I, N-1, EFun)];
        none ->
            []
    end.

TF是遍历函数。该qlc模块要求有一种遍历数据结构的所有对象的方法。gb_trees具有适合于此目的的迭代器函数。注意,对于每个返回的对象,都会创建一个新的乐趣。只要列表没有被终止[],就认为列表的尾部是空函数,调用该函数会返回更多的对象(和函数)。

查找功能是可选的。假设查找函数总是找到比遍历表快得多的值。第一个参数是密钥的位置。正如qlc_next/1将对象作为{Key, Value}对返回,位置为1.请注意,查找函数将{Key, Value}按照遍历函数的方式返回对。

格式化功能也是可选的。它被称为info/1,2在运行时反馈如何评估查询。尽量提供尽可能好的反馈,而不要显示太多细节。在该示例中,最多显示了表格的七个对象。格式函数处理两种情况:all意味着表格的所有对象都被遍历;{lookup, 1, KeyValues}意味着查找功能用于查找关键值。

整个表是遍历的还是仅查找某些键都取决于查询的表达方式。如果查询具有表单

qlc:q([T || P <- LE, F])

并且P是一个元组,该qlc模块分析P并且F在编译时间中查找P与常量相等的测试元组的位置。如果运行时这样的位置结果是关键位置,则可以使用查找函数,否则必须遍历表的所有对象。info函数InfoFun返回键位置。也可以有索引位置,也可以通过info函数返回。索引是一个额外的表格,可以快速查找某个位置。Mnesia根据请求维护索引,并引入所谓的二级密钥。的qlc模块更倾向于使用辅助键之前的关键无论常数的数量的查找查找对象。

关键的平等

Erlang/OTP有两个用于测试术语相等的运算符:==/2=:=/2。不同之处在于可以用浮点数表示的整数。例如,2 == 2.0计算结果为true,而2 =:= 2.0计算结果为false。通常这是一个小问题,但qlc模块不能忽略差异,这会影响用户在QLC中选择运营商。

如果qlc模块在编译时可以确定某个常量没有整数,那么使用哪一个==/2或哪一个=:=/2是无关紧要的:

1> E1 = ets:new(t, [set]), % uses =:=/2 for key equality
Q1 = qlc:q([K ||
{K} <- ets:table(E1),
K == 2.71 orelse K == a]),
io:format("~s~n", [qlc:info(Q1)]).
ets:match_spec_run(lists:flatmap(fun(V) ->
                                        ets:lookup(20493, V)
                                 end,
                                 [a,2.71]),
                   ets:match_spec_compile([{{'$1'},[],['$1']}]))

在这个例子中,操作员的==/2处理和处理完全一样=:=/2。但是,如果在编译时无法确定某个常量没有整数,并且=:=/2在比较键的相等性时使用该表(请参阅选项key_equality),那么qlc模块不会尝试查找常量。原因在于,在一般情况下,可以比较等于这样一个常数的关键字的数量没有上限; 必须查看整数和浮点数的每个组合:

2> E2 = ets:new(t, [set]),
true = ets:insert(E2, [{{2,2},a},{{2,2.0},b},{{2.0,2},c}]),
F2 = fun(I) ->
qlc:q([V || {K,V} <- ets:table(E2), K == I])
end,
Q2 = F2({2,2}),
io:format("~s~n", [qlc:info(Q2)]).
ets:table(53264,
          [{traverse,
            {select,[{{'$1','$2'},[{'==','$1',{const,{2,2}}}],['$2']}]}}])
3> lists:sort(qlc:e(Q2)).
[a,b,c]

寻找{2,2}只将不会返回bc

如果表格==/2在比较键的相等时使用,qlc模块将查找常量,而不管QLC中使用哪个运算符。但是,这==/2是首选:

4> E3 = ets:new(t, [ordered_set]), % uses ==/2 for key equality
true = ets:insert(E3, [{{2,2.0},b}]),
F3 = fun(I) ->
qlc:q([V || {K,V} <- ets:table(E3), K == I])
end,
Q3 = F3({2,2}),
io:format("~s~n", [qlc:info(Q3)]).
ets:match_spec_run(ets:lookup(86033, {2,2}),
                   ets:match_spec_compile([{{'$1','$2'},[],['$2']}]))
5> qlc:e(Q3).
[b]

查找连接的处理过程类似于查找表中的常量:如果连接运算符是==/2,并且要查找常量的表使用了=:=/2测试键是否相等,则qlc模块不会考虑对该表进行查找连接。

数据类型

abstract_expr() = erl_parse:abstract_expr()

为Erlang表达式分析树,请参阅The Abstract FormatERTS用户指南中的部分。

answer() = term()

answers() = [answer()]

cache() = ets | list | no

match_expression() = ets:match_spec()

匹配规范,请参阅Match Specifications in ErlangERTS用户指南和ms_transform(3)

no_files() = integer() >= 1

一个大于1的整数。

key_pos() = integer() >= 1 | [integer() >= 1]

max_list_size() = integer() >= 0

order() = ascending | descending | order_fun()

order_fun() = fun((term(), term()) -> boolean())

query_cursor()

一个query cursor

query_handle()

一个query handle

query_handle_or_list() = query_handle() | list()

query_list_comprehension() = term()

一个文字query list comprehension

spawn_options() = default | [proc_lib:spawn_option()]

sort_options() = [sort_option()] | sort_option()

sort_option() =

    {compressed, boolean()} |

    {no_files, no_files()} |

    {order, order()} |

    {size, integer() >= 1} |

    {tmpdir, tmp_directory()} |

    {unique, boolean()}

See file_sorter(3).

tmp_directory() = [] | file:name()

tmp_file_usage() =

    allowed | not_allowed | info_msg | warning_msg | error_msg

出口

append(QHL) -> QH

类型

返回一个查询句柄。在评估查询句柄时QHQHL返回第一个查询句柄的所有答案,然后返回其余查询句柄的所有答案QHL

append(QH1, QH2) -> QH3

类型

返回一个查询句柄。评估查询句柄时QH3QH1返回所有答案,然后返回所有答案QH2

append(QH1, QH2)相当于append([QH1, QH2])

cursor(QH) -> Cursor

cursor(QH, Options) -> Cursor

类型

创建查询游标并使调用进程成为游标的所有者。游标将被用作next_answers/1,2和(最终)的参数delete_cursor/1。调用erlang:spawn_opt/2产生并链接到评估查询句柄的进程。spawn_options调用时,option的值被用作最后一个参数spawn_opt/2。默认为[link]

例:

1> QH = qlc:q([{X,Y} || X <- [a,b], Y <- [1,2]]),
QC = qlc:cursor(QH),
qlc:next_answers(QC, 1).
[{a,1}]
2> qlc:next_answers(QC, 1).
[{a,2}]
3> qlc:next_answers(QC, all_remaining).
[{b,1},{b,2}]
4> qlc:delete_cursor(QC).
ok

cursor(QH)相当于cursor(QH, [])

delete_cursor(QueryCursor) -> ok

类型

删除查询游标。只有游标的所有者才能删除游标。

e(QH) -> Answers | Error

e(QH, Options) -> Answers | Error

eval(QH) -> Answers | Error

eval(QH, Options) -> Answers | Error

类型

评估调用过程中的查询句柄并收集列表中的所有答案。

例:

1> QH = qlc:q([{X,Y} || X <- [a,b], Y <- [1,2]]),
qlc:eval(QH).
[{a,1},{a,2},{b,1},{b,2}]

eval(QH)相当于eval(QH, [])

fold(Function, Acc0, QH) -> Acc1 | Error

fold(Function, Acc0, QH, Options) -> Acc1 | Error

类型

Function连同一个额外的参数一起调用查询句柄的连续答案AccIn。查询句柄和函数在调用过程中进行评估。Function必须返回一个新的累加器,并将其传递给下一个调用。如果查询句柄没有答案,则返回Acc0

例:

1> QH = [1,2,3,4,5,6],
qlc:fold(fun(X, Sum) -> X + Sum end, 0, QH).
21

fold(Function, Acc0, QH)相当于fold(Function, Acc0, QH, [])

format_error(Error) -> Chars

类型

返回由qlc模块的某些函数或分析转换返回的错误元组的英文描述性字符串。该函数主要由调用解析变换的编译器使用。

info(QH) -> Info

info(QH, Options) -> Info

类型

返回有关查询句柄的信息。这些信息描述了准备查询查询结果的简化和优化。该功能可能在调试过程中非常有用。

该信息具有Erlang表达式的形式,其中QLC最有可能发生。根据所提到的QLC表格的格式功能,这些信息不完全准确。

选项:

  • 缺省值是在块中返回一系列{flat, false}QLC ,但如果指定了选项,则返回一个QLC。
  • 默认是返回一个字符串,但是如果{format, abstract_code}指定了选项,则会返回抽象代码。在抽象代码中,端口标识符,引用和pid由字符串表示。
  • 缺省值是返回列表中的所有元素,但是如果{n_elements, NElements}指定了选项,则只返回有限数量的元素。
  • 默认值是显示对象的所有部分并匹配规格,但是如果{depth, Depth}指定了选项,则部分特定深度以下的项目将被替换为'...'

info(QH)相当于info(QH, [])

例子:

在以下示例中,仅插入两个简单的QLC来保持选项{unique, true}

1> QH = qlc:q([{X,Y} || X <- [x,y], Y <- [a,b]]),
io:format("~s~n", [qlc:info(QH, unique_all)]).
begin
    V1 =
        qlc:q([
               SQV ||
                   SQV <- [x,y]
              ],
              [{unique,true}]),
    V2 =
        qlc:q([
               SQV ||
                   SQV <- [a,b]
              ],
              [{unique,true}]),
    qlc:q([
           {X,Y} ||
               X <- V1,
               Y <- V2
          ],
          [{unique,true}])
end

在以下示例中,V2插入了QLC 以显示已选联接的生成器和联接方法。约定用于查找连接:第一个generator(G2)是遍历的,第二个(G1)是查找常量的表。

1> E1 = ets:new(e1, []),
E2 = ets:new(e2, []),
true = ets:insert(E1, [{1,a},{2,b}]),
true = ets:insert(E2, [{a,1},{b,2}]),
Q = qlc:q([{X,Z,W} ||
{X, Z} <- ets:table(E1),
{W, Y} <- ets:table(E2),
X =:= Y]),
io:format("~s~n", [qlc:info(Q)]).
begin
    V1 =
        qlc:q([
               P0 ||
                   P0 = {W,Y} <- ets:table(17)
              ]),
    V2 =
        qlc:q([
               [G1|G2] ||
                   G2 <- V1,
                   G1 <- ets:table(16),
                   element(2, G1) =:= element(1, G2)
              ],
              [{join,lookup}]),
    qlc:q([
           {X,Z,W} ||
               [{X,Z}|{W,Y}] <- V2
          ])
end

keysort(KeyPos, QH1) -> QH2

keysort(KeyPos, QH1, SortOptions) -> QH2

类型

返回一个查询句柄。在评估查询句柄时QH2,查询句柄的答案根据选项QH1进行排序file_sorter:keysort/4

分拣机仅在QH1不计算列表的情况下使用临时文件,并且答案的二进制表示的大小超过Size字节,其中Size是选项的值size

keysort(KeyPos, QH1)相当于keysort(KeyPos, QH1, [])

next_answers(QueryCursor) -> Answers | Error

next_answers(QueryCursor, NumberOfAnswers) -> Answers | Error

类型

返回查询游标的其余部分或全部答案。只有拥有者QueryCursor才能检索答案。

可选参数NumberOfAnswers确定返回的最大答案数。默认为10。如果返回比回答的请求数量少,后续调用next_answers返回[]

q(QLC) -> QH

q(QLC, Options) -> QH

类型

返回QLC的查询句柄。QLC必须是这个函数的第一个参数,否则它被评估为普通的列表理解。还需要将以下行添加到源代码中:

-include_lib("stdlib/include/qlc.hrl").

这会导致解析转换为QLC取代乐趣。当查询句柄被评估时,调用(编译的)乐趣被调用。

qlc:q/1,2从Erlang shell 调用时,会自动调用解析转换。当发生这种情况时,替换QLC的乐趣不会被编译,而是被评估erl_eval(3)。表达式file:eval/1,2在调试器中进行评估时也是如此。

为了明确,这不起作用:

...
A = [X || {X} <- [{1},{2}]],
QH = qlc:q(A),
...

变量A绑定到列表理解([1,2])的评估值。编译器抱怨错误消息(“参数不是查询列表理解”); shell进程停止了一个badarg原因。

q(QLC)相当于q(QLC, [])

选项:

  • 选项{cache, ets}可用于缓存QLC的答案。答案存储在每个缓存QLC的一个ETS表中。当再次评估高速缓存的QLC时,将从该表中提取答案,而无需进一步计算。因此,当找到对缓存的QLC的所有答案时,用于缓存对QLC限定词答案的ETS表可以被清空。选项cache相当于{cache, ets}
  • Option {cache, list}可以用来缓存QLC的答案{cache, ets}。不同之处在于答案保存在列表中(在过程堆上)。如果答案会占用超过一定量的RAM内存,则使用临时文件来存储答案。选项max_list_size以字节为单位设置限制,临时文件放在选项设置的目录中tmpdircache如果知道QLC至多要评估一次,则选项无效。对于最顶级的QLC以及限定符列表中第一个生成器的列表表达式,这总是如此。请注意,在过滤器或回调函数中存在副作用时,QLC的答案会受到选项的影响cache
  • 选项{unique, true}可用于删除QLC的重复答案。唯一的答案存储在每个QLC的一个ETS表中。每次知道QLC没有更多答案时,表格就会被清空。选项unique相当于{unique, true}。如果选项unique与选项结合使用{cache, ets},则使用两个ETS表格,但完整答案仅存储在一个表格中。如果选项unique与选项结合使用{cache, list},则使用keysort/3; 一次删除重复,一次恢复订单。

选项cacheunique不仅适用于QLC本身,还适用于查找常量,运行匹配规格和加入句柄的结果。

例:

在以下示例中,为每个值遍历合并连接的缓存结果A。请注意,如果没有选项cache,连接将进行三次,每次连接一次A

1> Q = qlc:q([{A,X,Z,W} ||
A <- [a,b,c],
{X,Z} <- [{a,1},{b,4},{c,6}],
{W,Y} <- [{2,a},{3,b},{4,c}],
X =:= Y],
{cache, list}),
io:format("~s~n", [qlc:info(Q)]).
begin
    V1 =
        qlc:q([
               P0 ||
                   P0 = {X,Z} <-
                       qlc:keysort(1, [{a,1},{b,4},{c,6}], [])
              ]),
    V2 =
        qlc:q([
               P0 ||
                   P0 = {W,Y} <-
                       qlc:keysort(2, [{2,a},{3,b},{4,c}], [])
              ]),
    V3 =
        qlc:q([
               [G1|G2] ||
                   G1 <- V1,
                   G2 <- V2,
                   element(1, G1) == element(2, G2)
              ],
              [{join,merge},{cache,list}]),
    qlc:q([
           {A,X,Z,W} ||
               A <- [a,b,c],
               [{X,Z}|{W,Y}] <- V3,
               X =:= Y
          ])
end

sort/1,2keysort/2,3还可以用于缓存的答案和删除重复。排序答案缓存在列表中时,可能存储在临时文件中,并且不使用ETS表。

有时(参见table/2)遍历表可以通过查找键值来完成,假定键值很快。在某些(罕见的)情况下,可能有太多关键值要查找。{max_lookup, MaxLookup}然后可以使用Option 来限制查找次数:如果MaxLookup要求查找的次数多于查找次数,则不会执行查找,而是会遍历表。默认为infinity,这意味着查找键的数量没有限制。

例:

在下面的例子中,使用gb_table由段模块Implementing a QLC Table中,有六个键查找:{1,a}{1,b}{1,c}{2,a}{2,b},和{2,c}。原因是密钥的两个元素{X, Y}分别进行比较。

1> T = gb_trees:empty(),
QH = qlc:q([X || {{X,Y},_} <- gb_table:table(T),
((X == 1) or (X == 2)) andalso
((Y == a) or (Y == b) or (Y == c))]),
io:format("~s~n", [qlc:info(QH)]).
ets:match_spec_run(
       lists:flatmap(fun(K) ->
                            case
                                gb_trees:lookup(K,
                                                gb_trees:from_orddict([]))
                            of
                                {value,V} ->
                                    [{K,V}];
                                none ->
                                    []
                            end
                     end,
                     [{1,a},{1,b},{1,c},{2,a},{2,b},{2,c}]),
       ets:match_spec_compile([{{{'$1','$2'},'_'},[],['$1']}]))

选项:

  • 选项{lookup, true}可以用来确保qlc模块在某个QLC表中查找常量。如果在生成器的列表表达式中有多个QLC表,则必须在至少一个表中查找常量。如果没有要查找的常量,则查询的评估失败。如果遍历某些表中的所有对象是不可接受的,则此选项很有用。设置选项lookupfalse确保不查找常量({max_lookup, 0}具有相同的效果)。默认为any,这意味着只要有可能就查找常量。
  • 选项 {join, Join}可以用来确保使用某个连接方法:
 - “{join,lookup}”调用查找联接方法。
- “{join,merge}”调用合并连接方法。
- `{join,nested_loop}`调用匹配来自两个句柄的每一对对象的方法。这种方法大多非常慢。

如果qlc模块无法执行所选的连接方法,则查询的评估失败。默认为any,这意味着如果可能的话使用一些快速连接方法。

sort(QH1) -> QH2

sort(QH1, SortOptions) -> QH2

类型

返回一个查询句柄。在评估查询句柄时QH2,查询句柄的答案根据选项QH1进行排序file_sorter:sort/3

分拣机仅在QH1不计算列表的情况下使用临时文件,并且答案的二进制表示的大小超过Size字节,其中Size是选项的值size

sort(QH1)相当于sort(QH1, [])

string_to_handle(QueryString) -> QH | Error

string_to_handle(QueryString, Options) -> QH | Error

string_to_handle(QueryString, Options, Bindings) -> QH | Error

类型

一个字符串版本q/1,2。当查询句柄被评估时,由解析转换创建的乐趣被解释erl_eval(3)。查询字符串是以句点结尾的单个QLC。

例:

1> L = [1,2,3],
Bs = erl_eval:add_binding('L', L, erl_eval:new_bindings()),
QH = qlc:string_to_handle("[X+1 || X <- L].", [], Bs),
qlc:eval(QH).
[2,3,4]

string_to_handle(QueryString)相当于string_to_handle(QueryString, [])

string_to_handle(QueryString, Options)相当于string_to_handle(QueryString, Options, erl_eval:new_bindings())

从Erlang之外调用这个函数可能主要是有用的,例如从用C编写的驱动程序调用。

table(TraverseFun, Options) -> QH

类型

返回QLC表的查询句柄。在Erlang/OTP中,支持ETS,Dets和Mnesia表,但许多其他数据结构可以转换为QLC表。这是通过让实现数据结构的模块中的函数通过调用创建查询句柄来完成的qlc:table/2。遍历表和表的属性的不同方式由作为选项提供的回调函数来处理qlc:table/2

  • 回调函数TraverseFun用于遍历表格。它返回一个由任一个[]或一个无限乐趣终止的对象列表,用于遍历表中尚未遍历的对象。任何其他返回值将立即作为查询评估的值返回。一元TraverseFun组将接受匹配规范作为参数。匹配规范是通过解析变换创建的,它qlc:table/2使用模式中引入的变量分析生成器调用和过滤器的模式。如果解析变换无法找到与模式和过滤器等效的匹配规范,TraverseFun则用匹配规范调用返回每个对象。
 - 可以使用匹配规范优化遍历表的模块是使用一个一元`TraverseFun`调用`qlc:table / 2`。一个例子是[`ets:table / 2`](ets#table-2)。
 - 其他模块可以提供无限的`TraverseFun`。一个例子是``实现QLC表格``中的`gb_table:table / 1`(about:blank#实施_a_qlc_table)。
  • PreFun在第一次读取表之前调用一次回调函数。如果呼叫失败,查询评估失败。参数PreArgs是标记值的列表。有两个标签,parent_value并且stop_fun,管理事务使用的Mnesia。
 - “parent_value”的值是由“ParentFun”返回的值,如果没有“ParentFun”,则为“undefined”。在调用[`eval / 1,2`](about:blank#eval-1),['fold / 3,4`]()的过程的上下文中调用`PreFun`之前调用`ParentFun` about:blank#fold-3)或[`cursor / 1,2`](about:blank#cursor-1)。
 - “stop_fun”的值是一个无用的乐趣,如果从父级调用,则删除该游标;如果没有游标,则为undefined。
  • PostFun在上次读取表后,Nullary回调函数被调用一次。被捕获的返回值被忽略。如果PreFun已经调用了表,PostFun那么即使查询的评估由于某种原因而失败,也会保证为该表调用。不同表格的预(后)函数以未指定的顺序进行评估。除阅读之外的其他表格访问,如调用InfoFun,在任何时候都假定为正常。
  • 二进制回调函数LookupFun用于查找表中的对象。第一个参数Position是关键位置或索引位置,第二个参数Keys是唯一值的排序列表。返回值是所有对象(元组)的列表,这样元素Position就是其中的成员Keys。任何其他返回值将立即作为查询评估的值返回。LookupFun被调用,而不是遍历表,如果在编译时的分析转换可以确定过滤器匹配并比较元素Position在这样一种方式,只Keys需要查找所有潜在的答案。关键位置通过调用InfoFun(keypos)和索引位置获得InfoFun(indices)。如果关键位置可用于查找,则始终选择关键位置,否则将选择需要最少查找次数的索引位置。如果两个索引位置之间存在联系,InfoFun则选择返回列表中第一个出现的位置。需要比max_lookup查找更多的职位将被忽略。
  • 一元回调函数InfoFun是返回关于表的信息。undefined如果某个标签的值未知,则indices返回: 返回索引位置列表,即正整数列表。如果is_unique_objects返回true的对象TraverseFun是唯一的,则返回。 keypos返回表键的位置,一个正整数。 is_sorted_key返回返回true的对象TraverseFun是否按键排序。 num_of_objects返回表中的对象数,一个非负整数。
  • 一元回调函数FormatFun所使用的info/1,2显示创建该表的查询处理呼叫。默认为undefined,这意味着info/1,2显示一个电话'$MOD':'$FUN'/0。需要FormatFun以适当的方式呈现表格的选定对象。但是,如果选择一个字符列表进行演示,它必须是可以扫描和解析的Erlang表达式(info/1,2尽管可以添加尾随点)。 FormatFun被称为参数,该参数根据分析qlc:table/2发生调用的QLC的过滤器所做的优化来描述所选对象。参数可以具有以下值: {lookup, Position, Keys, NElements, DepthFun}**用于在表格中查找对象。 LookupFun{match_spec, MatchExpression} 找不到通过查找关键字找到所有可能的答案的方法,但可以将过滤器转换为匹配规范。所有答案都可以通过调用找到TraverseFun(MatchExpression){all, NElements, DepthFun}**没有发现优化。如果TraverseFun是一元的,则使用匹配所有对象的匹配规范。 NElementsinfo/1,2选项的价值n_elementsDepthFun是可用于限制术语大小的函数; 将DepthFun(Term)替代品称为选项所指定深度以下的'...'部分。如果使用包含和失败的参数进行调用,则会使用不包含和(或)的参数再次调用。 Terminfo/1,2depthFormatFunNElementsDepthFunFormatFunNElementsDepthFun{lookup, Position, Keys}all
  • 选项的值key_equality'=:='如果表格认为两个关键字相等(如果它们匹配的话),并且'=='如果两个关键字相等(如果它们相等则相等)。默认为'=:='

对于由认可的各种选择table/1,2相应的模块中,见ets(3)dets(3)mnesia(3)

另请参阅

dets(3), erl_eval(3), erlang(3), error_logger(3), ets(3), file(3), file_sorter(3), mnesia(3), shell(3), Erlang Reference Manual, Programming Examples

Erlang 20

Erlang 是一种通用的面向并发的编程语言,可应付大规模开发活动的程序设计语言和运行环境。

主页 https://www.erlang.org/
源码 https://github.com/erlang/otp
版本 20
发布版本 20.1