2023年政策修订增补工作正在进行中,欢迎参与!
  • Moegirl.ICU:萌娘百科流亡社群 581077156(QQ),欢迎对萌娘百科运营感到失望的编辑者加入
  • Moegirl.ICU:账号认领正在试运行,有意者请参照账号认领流程

Module:Luaq/doc

萌娘百科,万物皆可萌的百科全书!转载请标注来源页面的网页链接,并声明引自萌娘百科。内容不可商用。
跳转到导航 跳转到搜索

这是Module:Luaq的文档页面

LUAQ是Lua Query的缩写,通过一系列查询函数让以Lua表(table)为主的数据源查询代码更加简洁。

使用配置

在模块代码中添加如下代码:

require("Module:Luaq")

之后便可以通过luaq使用各种查询函数。

使用

本章节将会介绍LUAQ的基本概念、查询函数的文档说明以及使用时必须或可选的注意事项。

若您想要了解查询函数的实现细节,或者希望修改结构、添加更多查询函数,请参阅#开发章节。

基本概念

查询对象

查询对象是由LUAQ的查询函数按照某种规范生成的特殊对象,它的本质是Lua表,但由于其内部的结构对于使用者来说是未知的,所以无法通过一般的手段获取它的内部结构。

实际上,使用者没有必要尝试去获取它的内部结构,若要获取它包含的内容,请参阅#查询展开章节。

延迟查询

延迟查询是LUAQ的重要特征之一,它表现为——当查询对象被生成时,查询对象并不会立即展开。唯有调用某些查询函数时,查询对象才会被计算,并返回查询结果。

在“查询对象被生成时”和“调用立即展开查询对象的函数”之间,改变作为数据源的Lua表内容将对最终查询结果会造成影响;当查询对象被展开后,无论数据源发生任何改变,都不会改变查询结果。

#查询函数章节中,会立即展开查询对象的函数将注有说明。

查询展开

查询展开是指将包含了一系列指令的查询对象转化为结果的过程。一共有两种方式进行展开。

enum:

enum(query)

全局函数,返回展开查询对象的迭代器。

函数返回三个值:

  1. 迭代器函数
  2. 查询对象本身
  3. 0

迭代器用在泛型for循环中。

luaq.query:

请参阅luaq.query

查询函数调用格式

为了使代码更加直观简洁,LUAQ在Lua函数调用语法糖的基础上支持了更简洁的写法。以下两种写法是等价的:

函数:

luaq.<查询函数名称>(查询对象[, 参数1[, 参数2[..., 参数n]]])

语法糖:

查询对象:<查询函数名称>([, 参数1[, 参数2[..., 参数n]]])

当您使用了语法糖调用形式,并且遇到了莫名其妙的“函数“...”的第1个参数类型错误(应为table,实为...)。"”报错,不妨检查一下是否将:错写成了.

查询函数

luaq.query

luaq.query(query)

展开一个查询对象

这个函数将立即展开查询对象,并将查询结果包装为一个Lua表返回。
参数:
返回:
一个Lua表,其数值类型的键即为查询结果每一项的位置索引。

luaq.asQuery

luaq.asQuery(t)

将一个Lua表包装为查询对象

当参数t已经是查询对象时,将返回其本身。
参数:
  • t:一个Lua表。
返回:
一个查询对象
若参数t为一个查询对象时,函数将会返回t自身。
否则将会返回一个包装了t查询对象
错误:
  1. 函数“asQuery”的第1个参数类型错误(应为table,实为...)。":当参数t的值不是一个Lua表的时候抛出此错误。
示例:
  • 代码:
<strong class="error"><span class="scribunto-error" id="mw-scribunto-error-7ed85f88">脚本错误:函数“main”不存在。</span></strong>
  • 结果:
<strong class="error"><span class="scribunto-error" id="mw-scribunto-error-7ed85f88">脚本错误:函数“main”不存在。</span></strong>

luaq.select

luaq.select(query, func)

对旧查询对象的每一个项进行指定的转换,并将结果形成新的查询对象。

参数:
  • query:一个查询对象,其每一个项将要被转换。
  • func:对query的每一个项进行的转换函数。转换函数将会接受到两个参数值:
    • 第1个参数值:query的当前项。
    • 第2个参数值:从开始到当前的索引。
返回:
一个查询对象。它由转换后的项组成。
错误:
  1. bad argument #2 to 'select' (table expected, got ...):当参数func的值不是一个函数的时候抛出此错误。
示例1:

在本示例中,转换函数返回当前项的值的类型。

  • 代码:
<strong class="error"><span class="scribunto-error" id="mw-scribunto-error-7ed85f88">脚本错误:函数“main”不存在。</span></strong>
  • 结果:
<strong class="error"><span class="scribunto-error" id="mw-scribunto-error-7ed85f88">脚本错误:函数“main”不存在。</span></strong>
示例2:

在本示例中,luaq.select函数判断当前索引的奇偶性。若索引为奇数,则返回key字段的值;若索引为偶数,则返回value字段的值。

  • 代码:
local t = { 5, 4, 3, str = "string" , 2, 1}
local q = luaq.asQuery(t)
    :select(
        function(pair, i)
            if math.fmod(i, 2) == 1 then return pair.key
            else return pair.value
            end
        end
    )
local r = q:query()

for key, value in pairs(t) do
    print("key = "..key.."\tvalue = "..value)
end

print()
print("查询结果:")
for i, v in ipairs(r) do
    print(v)
end
  • 结果:
key = 1         value = 5
key = 2         value = 4
key = 3         value = 3
key = 4         value = 2
key = 5         value = 1
key = str       value = string

查询结果:
1
4
3
2
5
string

luaq.where

luaq.where(query, func)

对旧查询对象的每一个项进行指定的筛选,并将符合条件的结果形成新的查询对象。

参数:
  • query:一个查询对象,其每一个项将要被筛选。
  • func:对query的每一个项进行检测的筛选函数。检测函数应返回truefalse,且将会接受到两个参数值:
    • 第1个参数值:query的当前项。
    • 第2个参数值:从开始到当前的索引。
返回:
一个查询对象。它由筛选后的项组成。
错误:
  1. bad argument #2 to 'where' (table expected, got ...):当参数func的值不是一个函数的时候抛出此错误。
示例:

本示例在luaq.select函数的示例1的基础上进行筛选。luaq.where函数的筛选机制为“项的值是字符串类型”或“项的值不小于3”。

  • 代码:
local t = { 5, 4, 3, str = "string" , 2, 1}
local q = luaq.asQuery(t)
    :select(
        function(pair)
            return type(pair.value)
        end
    )
    :where(
        function(e)
            return type(e) == "string" or e >= 3
        end
    )
local r = q:query()

for key, value in pairs(t) do
    print("key = "..key.."\tvalue = "..value)
end

print()
print("查询结果:")
for i, v in ipairs(r) do
    print(v)
end
  • 结果:
key = 1         value = 5
key = 2         value = 4
key = 3         value = 3
key = 4         value = 2
key = 5         value = 1
key = str       value = string

查询结果:
4
3
5
string

luaq.count

luaq.count(query)

获取查询结果中项的数量。

这个函数将立即展开查询对象。
参数:
  • query:一个查询对象。
返回:
查询结果中项的数量。
示例:

本示例同时演示了LUAQ的延迟查询。表中含有6对值键对,因此第一次luaq.count查询的结果为6。

删去表中的第二个元素后,已通过调用luaq.query展开查询的变量r,其循环遍历输出的内容不变,但第二次luaq.count查询的结果变为5。

  • 代码:
local t = { 5, 4, 3, str = "string" , 2, 1}
local q = luaq.asQuery(t)
local r = q:query()
for i, pair in ipairs(r) do
	print(pair.key.."\t"..pair.value)
end
print("count() = "..q:count())

print()
table.remove(t, 2) --删去表中的第二个元素
for i, pair in ipairs(r) do
	print(pair.key.."\t"..pair.value)
end
print("count() = "..q:count())
  • 结果:
1       5
2       4
3       3
4       2
5       1
str     string
count() = 6

1       5
2       4
3       3
4       2
5       1
str     string
count() = 5

luaq.any

luaq.all

luaq.distinct

luaq.groupBy

开发

本章节将会介绍LUAQ的实现细节、开发相关的文档说明等。

查询对象保留字段

查询对象本质是一个Lua表。为了判断一个Lua表是否为查询对象,同时为了实现LUAQ核心功能,该Lua表必须设置元表(metatable)并且遵守以下约束

  • __enum(必须)一个函数,返回查询对象的枚举器。
  • __reset(可选)一个函数,用于重置查询对象的状态。
  • __index(可选)一个函数,用于按数字索引获取对应的查询结果的项目。
  • __ipairs(可选)一个函数,用于重写_G.ipairs函数逻辑。
  • __pairs(可选)一个函数,用于重写_G.pairs函数逻辑。

除此之外,各个LUAQ查询函数生成的查询对象的元表也有各自的保留字段,请在开发中避免占用。

各查询函数的保留字段将分别说明。

枚举器

对每一个查询对象调用__getenumerator函数即可获得其枚举器。

枚举器是支持LUAQ延迟查询的基石,通过改变其内部的状态可以实现向前、向后查询等操作。

可以通过以下两种方式获取枚举器的对象:

  • 函数:

<查询对象>.__getenumerator(<查询对象>)

  • 语法糖:

<查询对象>:__getenumerator()

枚举器字段

枚举器是一个Lua表,为了正常工作,它必须具有以下字段:

  • current:这个字段的类型为函数,用以获取当前查询的项。
    调用:
    • 函数<枚举器对象>.current(self, query)
    • 语法糖<枚举器对象>:current(query)
    参数:
    self:即<枚举器对象>本身,用以支持语法糖调用。
    query:包含<枚举器对象>查询对象
    返回:
    当前查询的项。
  • moveLast:这个字段的类型为返回值为truefalse函数,用以将枚举器内部的状态向后回退一次。
    调用:
    • 函数<枚举器对象>.moveLast(self, query)
    • 语法糖<枚举器对象>:moveLast(query)
    参数:
    self:即<枚举器对象>本身,用以支持语法糖调用。
    query:包含<枚举器对象>查询对象
    返回:
    一个值,指示回退操作是否成功。
  • moveNext:这个字段的类型为返回值为truefalse函数,用以将枚举器内部的状态向前前进一次。
    调用:
    • 函数<枚举器对象>.moveNext(self, query)
    • 语法糖<枚举器对象>:moveNext(query)
    参数:
    self:即<枚举器对象>本身,用以支持语法糖调用。
    query:包含<枚举器对象>查询对象
    返回:
    一个值,指示前进操作是否成功。
  • saveState:这个字段的类型为函数,用以获取枚举器内部的当前状态。
    调用:
    • 函数<枚举器对象>.saveState(self, query)
    • 语法糖<枚举器对象>:saveState(query)
    参数:
    self:即<枚举器对象>本身,用以支持语法糖调用。
    query:包含<枚举器对象>查询对象
    返回:
    枚举器内部的当前状态。
  • resetState:这个字段的类型为函数,用以设置枚举器内部的当前状态。
    调用:
    • 函数<枚举器对象>.resetState(self, query, state)
    • 语法糖<枚举器对象>:resetState(query, state)
    参数:
    self:即<枚举器对象>本身,用以支持语法糖调用。
    query:包含<枚举器对象>查询对象
    state:要设置的枚举器内部的状态。

枚举器工作原理

  1. 获取查询对象query
  2. 通过query:__getenumerator()获取枚举器enumerator。除非特殊设计,一般的枚举器的初始状态为起点。
  3. 调用enumerator:moveNext(query)枚举器内部状态前进一次,若返回值为true,则继续下一步。
  4. 调用enumerator:current(query)获取当前的项current
  5. current进行后续处理。
示例:
local query = luaq.asQuery({ 5, 4, 3, 2, 1 })
local enumerator = query:__getenumerator()
while enumerator:moveNext(query) do
    local current = enumerator:current(query)
    print(current.key.."\t"..current.value)
end
结果:
1       5
2       4
3       3
4       2
5       1

查询函数实现细节

实现细节:linq.query