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

模組: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