Module:Sandbox/サンムル/Luaq
< Module:Sandbox | サンムル
跳到导航
跳到搜索
local luaq = { util = {} } function luaq.util.errBadArgType(funcName, paramIndex, expectedType, arg) error(mw.ustring.format("函数“%s”的第%d个参数类型错误(应为%s,实为%s)。", funcName, paramIndex, expectedType, type(arg))) end function luaq.util.errBadArgValue(funcName, paramIndex, expectedValue, arg, message, ...) local gotValue = type(arg) if gotValue == "number" or gotValue == "string" or gotValue == "boolean" then gotValue = mw.dumpObject(arg) end if type(message) == "string" then message = mw.ustring.format(message,expectedValue, arg, ...) else message = mw.ustring.format("(应为%s,实为%s)", expectedValue, mw.dumpObject(arg)) end error(mw.ustring.format("函数“%s”的第%d个参数的值错误%s。", funcName, paramIndex, message)) end function luaq.util.equal(l, r) -- 对键的每一个项目进行相等比较。 local maxIndex = math.max(select("#", unpack(l)), select("#", unpack(r))) for _index = 1, maxIndex do if l[_index] ~= r[_index] then return false end end return true end function luaq.util.compare(l, r) -- 对键的每一个项目进行大小比较。 local maxIndex = math.max(select("#", unpack(l)), select("#", unpack(r))) -- 越靠前的项目遇到不相等时,则两个键的大小关系等于这两个项目的大小关系。 for _index = 1, maxIndex do local le, re = l[_index], r[_index] if le < re then return -1 elseif le > re then return 1 end end return 0 -- 所有项目均相等,则两个键相等。 end local function isQuery(t) if type(t) ~= "table" then return false end local mt = getmetatable(t) return type(mt) == "table" and type(mt.__enum) == "function" and (mt.__reset == nil or type(mt.__reset) == "function") end -- 查询对象的默认索引元函数。(必须提供缓存元函数的实现。) local function __index(query, k) if type(k) == "number" and k > 0 then local self = getmetatable(query) self:__cachetill(k) -- 计算查询结果到索引k的项并缓存。 return self.__cache[k] end end -- 查询对象的默认枚举元函数。(必须提供索引元函数的实现。) local function __enum(query, index) index = index + 1 local current = query[index] if current then return index, current end end -- 查询对象的默认有序迭代元函数。 local function __ipairs(query) return getmetatable(query).__enum, query, 0 end -- 查询对象的默认无序迭代元函数。 local __pairs = __ipairs local function enext(query, index) local index, current = getmetatable(query).__enum(query, index) if current then return index, unpack(current) end end function _G.enum(query) if not isQuery(query) then luaq.util.errBadArgValue("enum", 1, "查询对象", t, "(不是%s)。") end return enext, query, 0 end function _G.resetstate(query) if not isQuery(query) then luaq.util.errBadArgValue("enum", 1, "查询对象", t, "(不是%s)。") end local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) -- 调用重置元函数。 end end -- 将一个Lua表的所有值键对包装成查询对象。 function luaq.asQuery(t) if type(t) ~= "table" then luaq.util.errBadArgType("asQuery", 1, "table", t) end if isQuery(t) then return t end -- 已经是查询对象了,不再重复操作。 local rtenum, rt, rtstate -- 内部表的枚举函数、固定值、以及初始状态。 local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if rtenum == nil then rtenum, rt, rtstate = _G.pairs(t) -- 获取内部表的枚举函数、固定值、以及初始状态。 end if type(rtenum) ~= "function" then luaq.util.errBadArgValue("asQuery", 1, nil, nil, "(全局pairs函数返回意外的值,找不到迭代函数)") end local state = c[#c] -- 当前起始状态为缓存数组的最末项。 if state then state = state[1] -- 取键为起始状态。 else state = rtstate -- 使用内部表提供的初始值为起始状态。 end local k, v = state, nil -- (优化) while index > #c do k, v = rtenum(rt, k) if k == nil then break end table.insert(c, { k, v }) -- 向缓存数组中插入新项。 end if k == nil then self.__cachedone = true end -- 仅当没有下一个键时设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = function(q) local self = getmetatable(q) self.__cache = {} self.__cachedone = false rtenum, rt, rtstate = nil, nil, nil end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end -- 将一个Lua表的第一个不包含nil的序列包装成查询对象。 function luaq.iasQuery(t) if type(t) ~= "table" then luaq.util.errBadArgType("iasQuery", 1, "table", t) end if isQuery(t) then return t end -- 已经是查询对象了,不再重复操作。 local rtenum, rt, rtstate -- 内部表的枚举函数、固定值、以及初始状态。 local mt = { __cache = { states = {} -- 保存状态的数组。 }, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if rtenum == nil then rtenum, rt, rtstate = _G.ipairs(t) -- 获取内部表的枚举函数、固定值、以及初始状态。 end if type(rtenum) ~= "function" then luaq.util.errBadArgValue("iasQuery", 1, nil, nil, "(全局ipairs函数返回意外的值,找不到迭代函数)") end local state, v if #c.states > 0 then state = c.states[#c.states] -- 当前起始状态为状态数组的最末项索引(或数组长度)。 else state = rtstate -- 使用内部表提供的初始值为起始状态。 end while index > #c do state, v = rtenum(rt, state) if v == nil then break end table.insert(c, { v }) -- 向缓存数组中插入新项。 table.insert(c.states, state) -- 向状态数组中插入新项。 end if v == nil then self.__cachedone = true end -- 仅当数组下一个项是nil时设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = function(q) local self = getmetatable(q) self.__cache = { states = {} } self.__cachedone = false rtenum, rt, rtstate = nil, nil, nil end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end -- 将可变参数序列包装成查询对象。 function luaq.asQueryFrom(...) local rt = { ... } local count = select("#", unpack(rt)) -- 获取可变参数序列的长度。 local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 local state = #c -- 当前起始状态为缓存数组的最末项索引(或数组长度)。 while index <= count and index > state do local v = rt[state + 1] table.insert(c, { v }) -- 向缓存数组中插入新项。 state = state + 1 end if state == count then self.__cachedone = true end -- 仅当缓存数组长度与可变参数序列长度一致时设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = nil -- 常量可变参数序列不再变化,无重置必要。 mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.select(query, func) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("select", 1, "查询对象", query, "(不是%s)。") end if type(func) ~= "function" then luaq.util.errBadArgType("select", 2, "function", func) end local iqenum, iq -- 内部查询对象的枚举元函数以及固定值。 local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if iqenum == nil then iqenum, iq = getmetatable(query).__enum, query -- 获取内部查询对象的枚举元函数以及固定值。 end local state, next = #c, nil -- 当前起始状态为缓存数组的最末项索引(或数组长度)。 while index > #c do state, next = iqenum(iq, state) if next == nil then break end next = { func(state, unpack(next)) } -- 调用选择函数。 table.insert(c, next) -- 向缓存数组中插入新项。 end if next == nil then self.__cachedone = true end -- 仅当数组下一个项是nil时设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq = nil, nil end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.selectMany(query, func) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("selectMany", 1, "查询对象", t, "(不是%s)。") end if func ~= nil and type(func) ~= "function" then luaq.util.errBadArgType("selectMany", 2, "nil或function", func) end local iqenum, iq, iqstate -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local tqenum, tq, tqstate -- 临时查询对象的枚举元函数、固定值以及迭代器的起始状态。 local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if iqenum == nil then iqenum, iq, iqstate = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 iqstate, tq = iqenum(iq, iqstate) end local next while index > #c do if tq == nil then break end if tqenum == nil then if func == nil then tq = unpack(tq) -- 拆包,获取元组第一个项目,进行下一步处理。 else tq = { func(iqstate, unpack(tq)) } end if type(tq) == "table" then if not isQuery(tq) then tq = luaq.iasQuery(tq) end else error(mw.ustring.format("处理序列中的第%d项时发生错误(不是查询对象)。", iqstate)) end tqenum, tqstate = getmetatable(tq).__enum, 0 -- 获取临时查询对象的枚举元函数以及迭代器的起始状态。 end tqstate, next = tqenum(tq, tqstate) if next == nil then iqstate, tq = iqenum(iq, iqstate) -- 获取内部查询对象的枚举元函数以及迭代器的起始状态。 tqenum, tqstate = nil, nil else table.insert(c, next) -- 向缓存数组中插入新项。 end end if tq == nil then self.__cachedone = true end -- 仅当数组下一个项是nil时设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq, iqstate = nil, nil, nil tqenum, tq, tqstate = nil, nil, nil end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.selectYield(query, func) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("select", 1, "查询对象", query, "(不是%s)。") end if type(func) ~= "function" then luaq.util.errBadArgType("select", 2, "function", func) end local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local mt = { __cache = {}, __cachedone = false } local yield = function(...) table.insert(mt.__cache, { ... }) end -- 迭代器产出函数。 mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if iqenum == nil then iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 end local next while index > #c do state, next = iqenum(iq, state) if next == nil then break end func(yield, state, unpack(next)) -- 调用迭代函数。 end if next == nil then self.__cachedone = true end -- 仅当数组下一个项是nil时设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq, state = nil, nil, nil end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.where(query, func) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("where", 1, "查询对象", query, "(不是%s)。") end if type(func) ~= "function" then luaq.util.errBadArgType("where", 2, "function", func) end local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if iqenum == nil then iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 end local next = nil while index > #c do state, next = iqenum(iq, state) if next == nil then break end if func(state, unpack(next)) then -- 调用筛选函数。 table.insert(c, { unpack(next) }) -- 向缓存数组中插入新项。 end end if next == nil then self.__cachedone = true end -- 仅当数组下一个项是nil时设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset if __reset then __reset(query) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq, state = nil, nil, nil end mt.__ipairs =__ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end -- 创建一个空的查询对象。 function luaq.empty() local mt = {} mt.__enum = function(q, index) return nil end mt.__reset = nil mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end -- 获取一个查询对象的结果的项个数。 function luaq.count(query) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("count", 1, "查询对象", t, "(不是%s)。") end local count = 0 for _ in enum(query) do count = count + 1 end return count end function luaq.any(query, func) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("any", 1, "查询对象", t, "(不是%s)。") end local qenum = getmetatable(query).__enum -- 获取枚举元函数。 if type(func) == "nil" then local _, v = qenum(query, 0) return v ~= nil elseif type(func) ~= "function" then luaq.util.errBadArgType("any", 2, "function", func) end local i, v = qenum(query, 0) while v ~= nil do if func(unpack(v)) then return true end i, v = qenum(query, i) end return false end function luaq.all(query, func) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("all", 1, "查询对象", t, "(不是%s)。") end if type(func) ~= "function" then luaq.util.errBadArgType("all", 2, "function", func) end local qenum = getmetatable(query).__enum -- 获取枚举元函数。 local i, v = qenum(query, 0) while v ~= nil do if not func(unpack(v)) then return false end i, v = qenum(query, i) end return true end function luaq.groupBy(query, keySelectFunc, entrySelectFunc, keyEqualityFunc) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("groupBy", 1, "查询对象", query, "(不是%s)。") end if type(keySelectFunc) ~= "function" then luaq.util.errBadArgType("groupBy", 2, "function", keySelectFunc) end if entrySelectFunc ~= nil and type(entrySelectFunc) ~= "function" then luaq.util.errBadArgType("groupBy", 3, "nil或function", entrySelectFunc) end if keyEqualityFunc ~= nil and type(keyEqualityFunc) ~= "function" then luaq.util.errBadArgType("groupBy", 4, "nil或function", keyEqualityFunc) end local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local mapcache = { -- 缓存所有项到键数组索引的映射。 keys = {}, -- 储存所有键的数组。 cachedone = false, cachenext = function(self) if self.cachedone then return end -- 如果已缓存完毕则退出。 if iqenum == nil then iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 end local next = nil state, next = iqenum(iq, state) if next == nil then self.cachedone = true -- 仅当数组下一个项是nil时设置缓存完毕标识符。 mw.log("mapcache is done") return end local knext = { keySelectFunc(state, unpack(next)) } -- 调用键选择函数。 -- 遍历键数组中所有已有键,与新键进行相等比较判断是否相等。 local hasKey = false for _keyindex, _key in ipairs(self.keys) do local keyTest -- 键相等检查 if keyEqualityFunc == nil then keyTest = luaq.util.equal(_key, knext) else keyTest = keyEqualityFunc(_key, knext) end if keyTest then -- 找到已有的相同的键,则将当前项归组至这个键。 hasKey = true table.insert(self, { _keyindex, next }) -- 缓存数组中保存指向键数组的索引+项。 break end end if not hasKey then -- 未找到已有的相同的键,则创建新键以归组当前项。 table.insert(self.keys, knext) -- 向键数组插入一个不同的键。 table.insert(self, { #self.keys, next }) -- 向缓存数组插入指向键数组末尾的索引+项。 end mw.log("mapcache is caching:", "entries["..#self.."]", "keys["..#self.keys.."]") end, reset = function(self) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 self.keys = {} -- 重置键数组 self.cachedone = false -- 重置缓存完毕标识符。 local length = #self for i = 1, length do self[i] = nil end -- 清空映射缓存。 iqenum, iq, state = nil, nil, nil end } local gmt = { __cache = {}, __cachedone = false } gmt.__index = __index gmt.__cachetill = function(gself, gindex) if gself.__cachedone then return end -- 如果已缓存完毕则退出。 if type(gindex) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", gindex) end if gindex <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", gindex) end local gc = gself.__cache -- 获取缓存数组。 if gindex <= #gc then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 while gindex > #gc do local nextKeyIndex = #gc + 1 -- 获取下一个键的索引。 local nextKey = mapcache.keys[nextKeyIndex] -- 获取下一个键。 while nextKey == nil and not mapcache.cachedone do -- 获取键时映射缓存未完成。 -- 重复向后缓存一个项并检查是否得到下一个键。 mapcache:cachenext() nextKey = mapcache.keys[nextKeyIndex] end if nextKey == nil then gself.__cachedone = true -- 映射缓存完毕时仍未找到下一个键,仅当此时设置缓存完毕标识符。 break end local nextEntryIndex = 1 -- 获取下一个项的索引。 local emt= { __cache = {}, __cachedone = false } emt.__index = __index emt.__cachetill = function(eself, eindex) if eself.__cachedone then return end -- 如果已缓存完毕则退出。 if type(eindex) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", eindex) end if eindex <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", eindex) end local ec = eself.__cache -- 获取缓存数组。 if eindex <= #ec then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 while eindex > #ec do local nextEntry = mapcache[nextEntryIndex] -- 获取下一个项。 while (nextEntry == nil or nextEntry[1] ~= nextKeyIndex) do -- 获得的下个项不符合条件。 -- 重复向后缓存一个项并检查是否得到下一个符合条件的项。 if nextEntry == nil then -- 若当前索引位置未缓存,则缓存一个位置。 if mapcache.cachedone then break end -- 映射缓存已完毕。 mapcache:cachenext() else -- 若当前索引位置的项不符合条件,则尝试下一个索引位置。 nextEntryIndex = nextEntryIndex + 1 end nextEntry = mapcache[nextEntryIndex] end if nextEntry == nil then eself.__cachedone = true -- 映射缓存完毕时仍未找到下一个项,仅当此时设置缓存完毕标识符。 break end local newEntry if entrySelectFunc == nil then newEntry = { nextKey, unpack(nextEntry[2]) } else newEntry = { nextKey, entrySelectFunc(eindex, mapcache.keys[nextKeyIndex], unpack(nextEntry[2])) } -- 调用项选择函数。 end table.insert(ec, newEntry) -- 向缓存数组中插入新项。 nextEntryIndex = nextEntryIndex + 1 end end emt.__enum = __enum emt.__reset = function(q) -- 项查询对象的重置将不改变分组结果,仅重新获取项选择函数的返回值。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false nextEntryIndex = 1 end emt.__ipairs = __ipairs emt.__pairs = __pairs local eqResult = {} luaq.util.attachLuaq(eqResult, emt) -- 将项查询对象添加入缓存。 table.insert(gc, { nextKey, eqResult }) end end gmt.__enum = __enum gmt.__reset = function(q) mapcache:reset() -- 首先重置映射缓存。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false end gmt.__ipairs = __ipairs gmt.__pairs = __pairs local gqResult = {} luaq.util.attachLuaq(gqResult, gmt) return gqResult end local function isSort(sort) return type(sort) == "table" and type(sort.start) == "function" end local function isOrderedQuery(t) return isQuery(t) and type(getmetatable(t).__group) == "function" end -- 获取默认排序器。 local function getDefaultSort(stages) return { start = function(self, info, desc, ...) -- 冒泡排序。 local count = info.length(...) for i = 1, count do local swaped = false for j = 1, count - i do if (info.compare(j, j + 1, ...) < 0) == desc then swaped = info.swap(j, j + 1, ...) or swaped end end if not swaped then break end end end } end function luaq.orderBy(query, func, comp, desc, sort) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("orderBy", 1, "查询对象", t, "(不是%s)。") end if func ~= nil and type(func) ~= "function" then luaq.util.errBadArgType("orderBy", 2, "function", func) end if comp ~= nil and type(comp) ~= "function" then luaq.util.errBadArgType("orderBy", 3, "nil或function", comp) end if desc == nil then desc = false elseif type(desc) ~= "boolean" then luaq.util.errBadArgType("orderBy", 4, "nil或boolean", desc) end if sort == nil then sort = getDefaultSort() elseif not isSort(sort) then luaq.util.errBadArgValue("orderBy", 5, "排序器", t, "(不是%s)。") end local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local sortcache = {} -- 缓存排序内容等信息。 sortcache.cachedone = false -- 缓存完毕标识符。 sortcache.cacheall = function(self) if self.cachedone then return end -- 如果已缓存完毕则退出。 if iqenum == nil then iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 end local next = nil while true do state, next = iqenum(iq, state) if next == nil then break end -- 仅当数组下一个项是nil时循环终止。 local pair = {} -- 排序键值对。 if func == nil then pair = { next } -- 不做排序键选择,即将自身作为排序键。 else pair = { { func(state, unpack(next)) }, next } -- 调用排序键选择函数。 end -- 遍历缓存中所有已有项组,与下一项进行相等比较判断是否相等。 local isEven = false for _, _group in ipairs(self) do local current = _group[1][1] -- 从项组中获取当前要比较的项。 local evenTest -- 项相等检查 if comp == nil then evenTest = luaq.util.equal(current, pair[1]) else evenTest = comp(current, pair[1]) == 0 end if evenTest then -- 找到已有的相同的项组,则将当前项归组至这个组。 isEven = true table.insert(_group, pair) break end end if not isEven then -- 未找到已有的相同的项组,则创建新组以归组当前项。 table.insert(self, { pair }) end end self.cachedone = true -- 设置缓存完毕标识符 end sortcache.length = function() return #sortcache end sortcache.entryat = function(index) if type(index) ~= "number" then luaq.util.errBadArgType("entryat", 1, "number", index) end if index <= 0 then luaq.util.errBadArgValue("entryat", 1, "正整数", index) end local group = sortcache[index] -- 获取等值组。 if group == nil then return nil else return group[1][1] -- 等值组的首项的选择键。 end end sortcache.compare = function(index1, index2) if type(index1) ~= "number" then luaq.util.errBadArgType("compare", 1, "number", index1) end if index1 <= 0 then luaq.util.errBadArgValue("compare", 1, "正整数", index1) end if type(index2) ~= "number" then luaq.util.errBadArgType("compare", 2, "number", index2) end if index2 <= 0 then luaq.util.errBadArgValue("compare", 2, "正整数", index2) end local temp1, temp2 = sortcache[index1], sortcache[index2] if temp1 == nil and temp2 == nil then return 0 -- 两个索引均超出范围。 elseif temp1 == nil then return -1 -- 第一个索引均超出范围。 elseif temp2 == nil then return 1 -- 第二个索引均超出范围。 else temp1, temp2 = temp1[1][1], temp2[1][1] -- 等值组的首项的选择键。 if comp == nil then return luaq.util.compare(temp1, temp2) else return comp(temp1, temp2) end end end sortcache.swap = function(index1, index2) if type(index1) ~= "number" then luaq.util.errBadArgType("swap", 1, "number", index1) end if index1 <= 0 then luaq.util.errBadArgValue("swap", 1, "正整数", index1) end if type(index2) ~= "number" then luaq.util.errBadArgType("swap", 2, "number", index2) end if index2 <= 0 then luaq.util.errBadArgValue("swap", 2, "正整数", index2) end local temp1, temp2 = sortcache[index1], sortcache[index2] if temp1 == nil or temp2 == nil then return false end -- 其中任何一项索引超出范围则返回不成功。 sortcache[index1], sortcache[index2] = temp2, temp1 return true -- 交换操作成功。 end sortcache.setall = function(indexes) if type(indexes) ~= "table" then luaq.util.errBadArgType("setall", 1, "table", indexes) end local length = #sortcache for i = 1, length do indexes[i] = sortcache[indexes[i]] end for i = 1, length do sortcache[i] = indexes[i] end end sortcache.sortdone = false -- 排序完毕标识符。 sortcache.sort = function(self) if self.sortdone then return end self:cacheall() -- 缓存所有项。 assert(self.cachedone, "排序缓存未缓存完毕,无法进行排序。") -- 设置断言捕捉潜在的逻辑错误。排序操作必须在缓存完毕的基础上进行,否则将会产生无法预料的后果。 sort:start(self, desc) -- 调用排序器。 self.sortdone = true end sortcache.reset = function(self) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 self.cachedone = false -- 重置缓存完毕标识符。 self.sortdone = false -- 重置排序完毕标识符。 local length = #self for i = 1, length do self[i] = nil end -- 清空排序缓存。 iqenum, iq, state = nil, nil, 0 end local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 sortcache:sort() -- 排序所有项。 assert(sortcache.sortdone, "排序缓存未排序完毕,无法进行结果缓存。") -- 设置断言捕捉潜在的逻辑错误。结果缓存操作必须在排序完毕的基础上进行,否则将会产生无法预料的后果。 -- 缓存所有结果。 for _, group in ipairs(sortcache) do for _, entry in ipairs(group) do table.insert(c, entry[2] or entry[1]) end end self.__cachedone = true -- 设置缓存完毕标识符。 end mt.__group = function(q, index) sortcache:sort() -- 排序所有项。 assert(sortcache.sortdone, "排序缓存未排序完毕,无法进行排序组枚举。") -- 设置断言捕捉潜在的逻辑错误。排序组枚举操作必须在排序完毕的基础上进行,否则将会产生无法预料的后果。 index = index + 1 local current = sortcache[index] if current then local group = {} for _, current in ipairs(current) do table.insert(group, current[2] or current[1]) end assert(#group > 0, "空的等值组。") -- 不应出现空的等值组。 return index, group end end mt.__enum = __enum mt.__reset = function(q) sortcache:reset() -- 首先重置排序缓存。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.thenBy(query, func, comp, desc, sort) if not isOrderedQuery(query) then luaq.util.errBadArgValue("thenBy", 1, "有序查询对象", t, "(不是%s)。") end if func ~= nil and type(func) ~= "function" then luaq.util.errBadArgType("thenBy", 2, "function", func) end if comp ~= nil and type(comp) ~= "function" then luaq.util.errBadArgType("thenBy", 3, "nil或function", comp) end if desc == nil then desc = false elseif type(desc) ~= "boolean" then luaq.util.errBadArgType("thenBy", 4, "nil或boolean", desc) end if sort == nil then sort = getDefaultSort() elseif not isSort(sort) then luaq.util.errBadArgValue("thenBy", 5, "排序器", t, "(不是%s)。") end local iqgroup, iq, state -- 内部查询对象的排序组元函数、固定值以及迭代器的起始状态。 local totalState = 0 -- 整体状态。 local sortcache = {} -- 缓存排序内容等信息。 sortcache.cachedone = false sortcache.cachenext = function(self) if self.cachedone then return false end -- 如果已缓存完毕则退出。 if iqgroup == nil then iqgroup, iq, state = getmetatable(query).__group, query, 0 -- 获取内部查询对象的排序组元函数、固定值以及迭代器的起始状态。 end local nextStage = nil state, nextStage = iqgroup(iq, state) if nextStage == nil then self.cachedone = true -- 仅当排序组下一个项是nil时设置缓存完毕标识符。 return false end local stage = {} -- 新建高阶排序阶。 for _, next in ipairs(nextStage) do local pair = {} -- 排序键值对。 if func == nil then pair = { next } -- 不做排序键选择,即将自身作为排序键。 else pair = { { func(totalState + state, unpack(next)) }, next } -- 调用排序键选择函数。 end -- 遍历新排序阶中所有已有项组,与下一项进行相等比较判断是否相等。 local isEven = false for _, _group in ipairs(stage) do local current = _group[1][1] -- 从项组中获取当前要比较的项。 local evenTest -- 项相等检查 if comp == nil then evenTest = luaq.util.equal(current, pair[1]) else evenTest = comp(current, pair[1]) == 0 end if evenTest then -- 找到已有的相同的项组,则将当前项归组至这个组。 isEven = true table.insert(_group, pair) break end end if not isEven then -- 未找到已有的相同的项组,则创建新组以归组当前项。 table.insert(stage, { pair }) end end table.insert(self, stage) -- 向排序缓存中添加新排序阶。 return true end sortcache.length = function(stage) if type(stage) == "number" then local s = sortcache[stage] if s == nil then return nil -- 索引超出范围。 else return #s -- 返回当前排序阶的长度。 end else -- 返回总长度。 local len = 0 for _, s in sortcache do len = len + #s end return len end end sortcache.entryat = function(index, stage) if type(index) ~= "number" then luaq.util.errBadArgType("entryat", 1, "number", index) end if index <= 0 then luaq.util.errBadArgValue("entryat", 1, "正整数", index) end if type(stage) ~= "number" then luaq.util.errBadArgType("entryat", 2, "number", stage) end if stage <= 0 then luaq.util.errBadArgValue("entryat", 2, "正整数", stage) end stage = sortcache[stage] -- 获取排序阶。 if stage == nil then return nil end -- 索引超出范围。 local group = stage[index] -- 获取等值组。 if group == nil then return nil else return group[1][1] -- 等值组的首项的选择键。 end end sortcache.compare = function(index1, index2, stage) if type(index1) ~= "number" then luaq.util.errBadArgType("compare", 1, "number", index1) end if index1 <= 0 then luaq.util.errBadArgValue("compare", 1, "正整数", index1) end if type(index2) ~= "number" then luaq.util.errBadArgType("compare", 2, "number", index2) end if index2 <= 0 then luaq.util.errBadArgValue("compare", 2, "正整数", index2) end if type(stage) ~= "number" then luaq.util.errBadArgType("compare", 3, "number", stage) end if stage <= 0 then luaq.util.errBadArgValue("compare", 3, "正整数", stage) end stage = sortcache[stage] -- 获取排序阶。 if stage == nil then return 0 end -- 索引超出范围。 local temp1, temp2 = stage[index1], stage[index2] if temp1 == nil and temp2 == nil then return 0 -- 两个索引均超出范围。 elseif temp1 == nil then return -1 -- 第一个索引均超出范围。 elseif temp2 == nil then return 1 -- 第二个索引均超出范围。 else temp1, temp2 = temp1[1][1], temp2[1][1] -- 等值组的首项的选择键。 if comp == nil then return luaq.util.compare(temp1, temp2) else return comp(temp1, temp2) end end end sortcache.swap = function(index1, index2, stage) if type(index1) ~= "number" then luaq.util.errBadArgType("swap", 1, "number", index1) end if index1 <= 0 then luaq.util.errBadArgValue("swap", 1, "正整数", index1) end if type(index2) ~= "number" then luaq.util.errBadArgType("swap", 2, "number", index2) end if index2 <= 0 then luaq.util.errBadArgValue("swap", 2, "正整数", index2) end if type(stage) ~= "number" then luaq.util.errBadArgType("swap", 3, "number", stage) end if stage <= 0 then luaq.util.errBadArgValue("swap", 3, "正整数", stage) end stage = sortcache[stage] -- 获取排序阶。 if stage == nil then return false end -- 索引超出范围。 local temp1, temp2 = stage[index1], stage[index2] if temp1 == nil or temp2 == nil then return false end -- 其中任何一项索引超出范围则返回不成功。 stage[index1], stage[index2] = temp2, temp1 return true -- 交换操作成功。 end sortcache.setall = function(indexes, stage) if type(indexes) ~= "table" then luaq.util.errBadArgType("setall", 1, "table", indexes) end stage = sortcache[stage] -- 获取排序阶。 if stage == nil then return end -- 索引超出范围。 local length = #stage for i = 1, length do indexes[i] = stage[indexes[i]] end for i = 1, length do stage[i] = indexes[i] end end sortcache.sortdone = false sortcache.sortnext = function(self) if self.sortdone then return false end local hasNext = self:cachenext() -- 缓存下一阶。 if not hasNext then self.sortdone = true return false end local stage = #self -- 获取下一阶的索引。 sort:start(self, desc, stage) return true end sortcache.reset = function(self) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 self.cachedone = false -- 重置缓存完毕标识符。 self.sortdone = false -- 重置排序完毕标识符。 local length = #self for i = 1, length do self[i] = nil end -- 清空映射缓存。 iqgroup, iq, state = nil, nil, 0 end local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 while index > #c and not sortcache.sortdone do if not sortcache:sortnext() then break end -- 排序下一阶。 local stage = sortcache[#sortcache] -- 获取下一阶。 -- 缓存下一阶的所有结果。 for _, group in ipairs(stage) do for _, entry in ipairs(group) do table.insert(c, entry[2] or entry[1]) end end end if sortcache.sortdone then self.__cachedone = true end -- 仅当排序缓存的所有阶均排序完毕时设置缓存完毕标识符。 end mt.__group = function(q, index) index = index + 1 local current = nil local offset, stage = 0, 1 while true do local s = sortcache[stage] if s == nil then -- 排序下一阶。 if not sortcache:sortnext() then break end s = sortcache[stage] if s == nil then break end end if index <= offset + #s then current = s[index - offset] break end offset = offset + #s stage = stage + 1 end if current then local group = {} for _, current in ipairs(current) do table.insert(group, current[2] or current[1]) end assert(#group > 0, "空的等值组。") -- 不应出现空的等值组。 return index, group end end mt.__enum = __enum mt.__reset = function(q) sortcache:reset() -- 首先重置排序缓存。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.distinct(query, func) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("distinct", 1, "查询对象", query, "(不是%s)。") end if func ~= nil and type(func) ~= "function" then luaq.util.errBadArgType("distinct", 2, "nil或function", func) end local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if iqenum == nil then iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 end local next = nil while index > #c do state, next = iqenum(iq, state) if next == nil then break end local isEven for _, current in ipairs(c) do if func then local evenTest = func(current, next) -- 调用筛选函数。 isEven = evenTest == true or evenTest == 0 else isEven = luaq.util.equal(current, next) end if isEven then break end end if not isEven then table.insert(c, { unpack(next) }) -- 向缓存数组中插入新项。 end end if next == nil then self.__cachedone = true end -- 仅当数组下一个项是nil时设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset if __reset then __reset(query) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq, state = nil, nil, nil end mt.__ipairs =__ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.concat(query, other) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("concat", 1, "查询对象", query, "(不是%s)。") end if type(other) == "table" then if not isQuery(other) then other = luaq.iasQuery(other) end else luaq.util.errBadArgValue("concat", 2, "查询对象", other, "(不是%s)。") end local iqenum1, iqenum2 -- 两个查询对象的枚举元函数。 local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 iqenum1 = iqenum1 or getmetatable(query).__enum iqenum2 = iqenum2 or getmetatable(other).__enum if iqenum == nil then iqenum, iq, state = iqenum1, query, 0 -- 获取第一个内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 end local next while index > #c do state, next = iqenum(iq, state) local continue = false if next == nil then if iqenum == iqenum2 then break else iqenum, iq, state = iqenum2, other, 0 -- 获取第二个内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 continue = true end end if not continue then table.insert(c, { unpack(next) }) -- 向缓存数组中插入新项。 end end if next == nil then self.__cachedone = true end -- 仅当数组下一个项是nil时设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset -- 获取第一个内部查询对象的重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 __reset = getmetatable(other).__reset -- 获取第二个内部查询对象的重置元函数。 if __reset then __reset(other) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq, state = nil, nil, nil end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.append(query, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("append", 1, "查询对象", query, "(不是%s)。") end if type(func) ~= "function" then luaq.util.errBadArgType("append", 2, "function", func) end local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local extra = { ... } -- 要追加的项。 local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if iqenum == nil then iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 end local next while index > #c do state, next = iqenum(iq, state) if next == nil then break end table.insert(c, { unpack(next) }) -- 向缓存数组中插入新项。 end if next == nil then -- 当数组下一个项是nil时。 table.insert(c, extra) -- 向缓存数组中插入追加项。 self.__cachedone = true -- 设置缓存完毕标识符 end end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq, state = nil, nil, nil end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.prepend(query, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("prepend", 1, "查询对象", query, "(不是%s)。") end if type(func) ~= "function" then luaq.util.errBadArgType("prepend", 2, "function", func) end local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local extra = { ... } -- 要追加的项。 local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if iqenum == nil then iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 table.insert(c, extra) -- 向缓存数组中插入追加项。 end local next while index > #c do state, next = iqenum(iq, state) if next == nil then break end table.insert(c, { unpack(next) }) -- 向缓存数组中插入新项。 end if next == nil then self.__cachedone = true end -- 仅当数组下一个项是nil时设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq, state = nil, nil, nil end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.reverse(query) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("reverse", 1, "查询对象", query, "(不是%s)。") end local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local mt = { __cache = {}, __cachedone = false } mt.__index = function(q, k) if type(k) == "number" and k > 0 then local self = getmetatable(q) self:__cacheall() -- 计算所有查询结果并缓存。 return self.__cache[#self.__cache - k + 1] end end mt.__cacheall = function(self) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if iqenum == nil then iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 end local c = self.__cache -- 获取缓存数组。 local next while true do state, next = iqenum(iq, state) if next == nil then break end table.insert(c, next) -- 向缓存数组中插入新项。 end self.__cachedone = true -- 设置缓存完毕标识符。 end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq, state = nil, nil, nil end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.aggregate(query, func, seed, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("aggregate", 1, "查询对象", query, "(不是%s)。") end if type(func) ~= "function" then luaq.util.errBadArgType("aggregate", 2, "function", func) end if seed ~= nil or type(seed) ~= "table" then luaq.util.errBadArgValue("aggregate", 2, "合法的元组", query, "(不是%s)。") end local iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local _, result = iqenum(iq, 0) local result, next = seed, nil while true do state, next = iqenum(iq, state) if next == nil then break end if result then result = { func(state - 1, result, state, next) } else result = next end end if result == nil then return ... else return unpack(result) end end function luaq.reserve(query, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("reserve", 1, "查询对象", query, "(不是%s)。") end local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local default = { ... } -- 要预留的项。 local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if iqenum == nil then iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 end local next while index > #c do state, next = iqenum(iq, state) if next == nil then break end table.insert(c, { unpack(next) }) -- 向缓存数组中插入新项。 end if next == nil then -- 当数组下一个项是nil时。 if #c == 0 then -- 内部查询对象为空(缓存为空) table.insert(c, extra) -- 向缓存数组中插入预留项。 end self.__cachedone = true -- 设置缓存完毕标识符 end end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq, state = nil, nil, nil end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.first(query, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("first", 1, "查询对象", query, "(不是%s)。") end local iqenum, iq = getmetatable(query).__enum, query -- 获取内部查询对象的枚举元函数以及固定值。 local _, result = iqenum(iq, 0) if result == nil then return ... else return unpack(result) end end function luaq.last(query, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("last", 1, "查询对象", query, "(不是%s)。") end local iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local result, next while true do state, next = iqenum(iq, state) if next == nil then break else result = next end end if result == nil then return ... else return unpack(result) end end function luaq.only(query, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("only", 1, "查询对象", query, "(不是%s)。") end local iqenum, iq = getmetatable(query).__enum, query -- 获取内部查询对象的枚举元函数以及固定值。 local state, result = iqenum(iq, 0) if result == nil or iqenum(iq, state) ~= nil then return ... -- 仅当不存在第一项或存在第二项时返回默认值。 else return unpack(result) end end function luaq.average(query, addition, division, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("average", 1, "查询对象", query, "(不是%s)。") end if addition == nil then division = nil -- 未提供求和函数时,使用默认的求和函数及求商函数。 elseif type(addition) ~= "function" then luaq.util.errBadArgType("average", 2, "nil或function", addition) end if division ~= nil and type(division) ~= "function" then luaq.util.errBadArgType("average", 3, "nil或function", division) end local iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local result, count, next while true do state, next = iqenum(iq, state) if next == nil then break end if result == nil then if addition == nil then local temp = {} local length = select("#", next) for _index = 1, length do temp[_index] = tonumber(next[_index]) end result = temp else result = next end else if addition == nil then local temp = {} local length = math.max(select("#", result), select("#", next)) for _index = 1, length do local v1, v2 = result[_index], tonumber(next[_index]) if v1 ~= nil and v2 ~= nil then temp[_index] = v1 + v2 else temp[_index] = v1 or v2 end end else result = addition(result, next) end end count = count + 1 end if result == nil then return ... end if division == nil then local length = select("#", result) for _index = 1, length do if result[_index] then result[_index] = result[_index] / count end end else result = division(unpack(result)) end return result end function luaq.max(query, comp, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("max", 1, "查询对象", query, "(不是%s)。") end if comp ~= nil and type(comp) ~= "function" then luaq.util.errBadArgType("max", 2, "nil或function", comp) end local iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local result, next while true do state, next = iqenum(iq, state) if next == nil then break end if result == nil then result = next else if comp == nil then if luaq.util.compare(result, next) < 0 then result = next end else local compTest = comp(result, next) if compTest == true or compTest < 0 then result = next end end end end if result == nil then return ... else return unpack(result) end end function luaq.min(query, comp, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("min", 1, "查询对象", query, "(不是%s)。") end if comp ~= nil and type(comp) ~= "function" then luaq.util.errBadArgType("min", 2, "nil或function", comp) end local iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local result, next while true do state, next = iqenum(iq, state) if next == nil then break end if result == nil then result = next else if comp == nil then if luaq.util.compare(result, next) > 0 then result = next end else local compTest = comp(next, result) if compTest == true or compTest > 0 then result = next end end end end if result == nil then return ... else return unpack(result) end end function luaq.sub(query, i, j) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("sub", 1, "查询对象", query, "(不是%s)。") end if type(i) ~= "number" then luaq.util.errBadArgType("sub", 2, "number", i) elseif i > 0 then i = math.ceil(i) elseif i < 0 then i = math.floor(i) else luaq.util.errBadArgValue("sub", 2, "非零的整数", i) end if j == nil then j = -1 elseif type(j) ~= "number" then luaq.util.errBadArgType("sub", 3, "nil或number", j) elseif j > 0 then j = math.floor(j) elseif j < 0 then j = math.ceil(j) else luaq.util.errBadArgValue("sub", 3, "非零的整数", j) end local iqenum, iq, state -- 内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local buff = {} local mt = { __cache = {}, __cachedone = false } mt.__index = __index mt.__cachetill = function(self, index) if self.__cachedone then return end -- 如果已缓存完毕则退出。 if type(index) ~= "number" then luaq.util.errBadArgType("__cachetill", 2, "number", index) end if index <= 0 then luaq.util.errBadArgValue("__cachetill", 2, "正整数", index) end local c = self.__cache -- 获取缓存数组。 if index <= #c then return end -- 如果指定索引小于已缓存项的数量,则直接退出。 if iqenum == nil then iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 end if ((i < 0) == (j < 0)) and i > j then -- 两个索引符号相同且起始索引大于结束索引,截取子串为空串。 self.__cachedone = true -- 设置缓存完毕标识符。 return end if i < 0 then -- 当起始索引为反向定位时,必须获得内部查询对象的所有项的数量。 -- 缓存所有项。 local count = 0 while true do local next state, next = iqenum(iq, state) if next == nil then break -- 缓存结束分支。 elseif j > 0 and count + i >= j then -- 计算得到起始索引大于结束索引。 self.__cachedone = true -- 设置缓存完毕标识符。 return end table.insert(buff, next) count = count + 1 end local v1, v2 = i, j if i < 0 then v1 = count + i + 1 end -- 获得正向定位起始索引。 if j < 0 then v2 = count + j + 1 end -- 获得正向定位结束索引。 for _index = v1, v2 do local e = buff[_index] if e == nil then break end table.insert(c, { unpack(e) }) -- 向缓存数组中插入新项。 end self.__cachedone = true -- 设置缓存完毕标识符。 else local v1, v2 = i, j while (j < 0 or state < j) and index > #c do local next if j < 0 then v2 = state + j + 2 end -- 获得正向定位结束索引。 state, next = iqenum(iq, state) if next == nil then self.__cachedone = true -- 仅当数组下一个项是nil时设置缓存完毕标识符。 if v1 > v2 then return -- 计算得到起始索引大于结束索引。 else break end elseif state >= v1 then if j > 0 then table.insert(c, { unpack(next) }) -- 向缓存数组中插入新项。 else table.insert(buff, next) if v2 >= v1 then table.insert(c, { unpack(buff[v2 - v1 + 1]) }) -- 向缓存数组中插入新项。 end end end end end end mt.__enum = __enum mt.__reset = function(q) local __reset = getmetatable(query).__reset -- 获取重置元函数。 if __reset then __reset(query) end -- 首先重置内部查询对象。 local self = getmetatable(q) self.__cache = {} self.__cachedone = false iqenum, iq, state = nil, nil, nil buff = {} end mt.__ipairs = __ipairs mt.__pairs = __pairs local qResult = {} luaq.util.attachLuaq(qResult, mt) return qResult end function luaq.skip(query, count, func) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("skip", 1, "查询对象", query, "(不是%s)。") end if type(count) ~= "number" then luaq.util.errBadArgType("skip", 2, "number", count) elseif count < 0 then luaq.util.errBadArgValue("skip", 2, "不是自然数", count) end if func ~= nil and type(func) ~= "function" then luaq.util.errBadArgType("skip", 3, "nil或function", func) end if func then query = query:where(func) end if count >= 0 then return query:sub(math.floor(count + 1)) else return query:sub(1, math.ceil(count - 1)) end end function luaq.take(query, count, func) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("take", 1, "查询对象", query, "(不是%s)。") end if type(count) ~= "number" then luaq.util.errBadArgType("take", 2, "number", count) elseif count < 0 then luaq.util.errBadArgValue("take", 2, "不是自然数", count) end if func ~= nil and type(func) ~= "function" then luaq.util.errBadArgType("take", 3, "nil或function", func) end if func then query = query:where(func) end if count >= 0 then return query:sub(1, math.floor(count)) else return query:sub(math.ceil(count)) end end function luaq.ofType(query, typename) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("ofType", 1, "查询对象", query, "(不是%s)。") end if type(typename) ~= "string" then luaq.util.errBadArgType("ofType", 2, "string", typename) end return query:where(function(_, e) return type(e) == typename end) end function luaq.query(query) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("query", 1, "查询对象", query, "(不是%s)。") end local tResult = {} for _, entry in enum(query) do table.insert(tResult, entry) end return tResult end function luaq.each(query, func) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("foreach", 1, "查询对象", query, "(不是%s)。") end if type(func) ~= "function" then luaq.util.errBadArgType("foreach", 2, "function", func) end local iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local next while true do state, next = iqenum(iq, state) if next == nil then break end func(state, unpack(next)) -- 调用操作函数。 end end function luaq.has(query, func, ...) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("has", 1, "查询对象", query, "(不是%s)。") end if func ~= nil and type(func) ~= "function" then luaq.util.errBadArgType("has", 2, "nil或function", func) end local iqenum, iq, state = getmetatable(query).__enum, query, 0 -- 获取内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local entry = { ... } local next while true do state, next = iqenum(iq, state) if next == nil then break end if func then local evenTest = func(entry, next) -- 调用筛选函数。 if evenTest == true or evenTest == 0 then return true end -- 找到相等项。 else if luaq.util.equal(entry, next) then return true end -- 找到相等项。 end end return false -- 未找到相等项。 end function luaq.is(query, other, func, sequence) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("is", 1, "查询对象", query, "(不是%s)。") end if type(other) == "table" then if not isQuery(other) then other = luaq.iasQuery(other) end else luaq.util.errBadArgValue("is", 2, "查询对象", other, "(不是%s)。") end if sequence == nil then sequence = true elseif type(sequence) ~= "boolean" then luaq.util.errBadArgType("is", 4, "nil或boolean", sequence) end if sequence then local iqenum1, iq1, state1 = getmetatable(query).__enum, query, 0 -- 获取第一个内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local iqenum2, iq2, state2 = getmetatable(other).__enum, other, 0 -- 获取另一个内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local next1, next2 while true do state1, next1 = iqenum1(iq1, state1) state2, next2 = iqenum2(iq2, state2) if next1 ~= nil and next2 ~= nil then local isEven if func then local evenTest = func(next1, next2) -- 调用筛选函数。 isEven = evenTest == true or evenTest == 0 else isEven = luaq.util.equal(next1, next2) end if not isEven then return false end elseif (next1 == nil) ~= (next2 == nil) then return false end if next1 == nil or next2 == nil then break end end return true else -- 获取两个结果表,若长度不一致则不相等。 local result1 = query:distinct(func):query() local result2 = other:distinct(func):query() if #result1 ~= #result2 then return false end for index1, entry1 in ipairs(result1) do for index2, entry2 in ipairs(result2) do if entry2 then -- 仅当entry2为table类型时才为真。(请不要轻易改动此表达式。) local isEven if func then local evenTest = func(entry1, entry2) -- 调用筛选函数。 isEven = evenTest == true or evenTest == 0 else isEven = luaq.util.equal(entry1, entry2) end if isEven then result2[index2] = false -- 使得外层if条件永远为假。(请不要轻易改动此值。) entry1 = nil -- 供外层for检测使用。(请不要轻易改动此值。) break end end end if entry1 then return false end -- 在内层for中未被设置为nil。(请不要轻易改动此表达式。) end return true end end local function isSubRelationship(query, other, func, sequence) if sequence then local iqenum1, iq1, state1 = getmetatable(query).__enum, query, 0 -- 获取第一个内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local iqenum2, iq2, state2 = getmetatable(other).__enum, other, 0 -- 获取另一个内部查询对象的枚举元函数、固定值以及迭代器的起始状态。 local next1, next2 state1, next1 = iqenum1(iq1, state1) state2, next2 = iqenum2(iq2, state2) local fstart = false -- 序列匹配开始标识符。 while true do if next1 == nil then return true -- 子序列匹配到结尾。 elseif next2 == nil then return false -- 子序列未匹配到结尾时,被检测序列到达结尾。 else local isEven if func then local evenTest = func(next1, next2) -- 调用筛选函数。 isEven = evenTest == true or evenTest == 0 else isEven = luaq.util.equal(next1, next2) end if isEven then if not fstart then fstart = true end -- 匹配开始。 state1, next1 = iqenum1(iq1, state1) -- 子序列匹配指针向后移动一项。 else if fstart then -- 复位子序列匹配指针以及状态。 fstart = false state1, next1 = iqenum1(iq1, 0) end end state2, next2 = iqenum2(iq2, state2) -- 被检测序列指针向后移动一项。 end end else -- 获取两个结果表。 local result1 = query:distinct(func):query() local result2 = other:query() for index1, entry1 in ipairs(result1) do for index2, entry2 in ipairs(result2) do if entry2 then -- 仅当entry2为table类型时才为真。(请不要轻易改动此表达式。) local isEven if func then local evenTest = func(entry1, entry2) -- 调用筛选函数。 isEven = evenTest == true or evenTest == 0 else isEven = luaq.util.equal(entry1, entry2) end if isEven then result2[index2] = false -- 使得外层if条件永远为假。(请不要轻易改动此值。) entry1 = nil -- 供外层for检测使用。(请不要轻易改动此值。) break end end end if entry1 then return false end -- 在内层for中未被设置为nil。(请不要轻易改动此表达式。) end return true end end function luaq.subOf(query, other, func, sequence) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("subOf", 1, "查询对象", query, "(不是%s)。") end if type(other) == "table" then if not isQuery(other) then other = luaq.iasQuery(other) end else luaq.util.errBadArgValue("subOf", 2, "查询对象", other, "(不是%s)。") end if sequence == nil then sequence = true elseif type(sequence) ~= "boolean" then luaq.util.errBadArgType("subOf", 4, "nil或boolean", sequence) end local rel = isSubRelationship(query, other, func, sequence) return rel end function luaq.superOf(query, other, func, sequence) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("superOf", 1, "查询对象", query, "(不是%s)。") end if type(other) == "table" then if not isQuery(other) then other = luaq.iasQuery(other) end else luaq.util.errBadArgValue("superOf", 2, "查询对象", other, "(不是%s)。") end if sequence == nil then sequence = true elseif type(sequence) ~= "boolean" then luaq.util.errBadArgType("superOf", 4, "nil或boolean", sequence) end local rel = isSubRelationship(other, query, func, sequence) return rel end function luaq.properSubOf(query, other, func, sequence) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("properSubOf", 1, "查询对象", query, "(不是%s)。") end if type(other) == "table" then if not isQuery(other) then other = luaq.iasQuery(other) end else luaq.util.errBadArgValue("properSubOf", 2, "查询对象", other, "(不是%s)。") end if sequence == nil then sequence = true elseif type(sequence) ~= "boolean" then luaq.util.errBadArgType("properSubOf", 4, "nil或boolean", sequence) end local rel, proper = isSubRelationship(query, other, func, sequence) return rel and proper end function luaq.properSuperOf(query, other, func, sequence) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("properSuperOf", 1, "查询对象", query, "(不是%s)。") end if type(other) == "table" then if not isQuery(other) then other = luaq.iasQuery(other) end else luaq.util.errBadArgValue("properSuperOf", 2, "查询对象", other, "(不是%s)。") end if sequence == nil then sequence = true elseif type(sequence) ~= "boolean" then luaq.util.errBadArgType("properSuperOf", 4, "nil或boolean", sequence) end local rel, proper = isSubRelationship(other, query, func, sequence) return rel and proper end function luaq.overlap(query, other, func, sequence) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("overlap", 1, "查询对象", query, "(不是%s)。") end if type(other) == "table" then if not isQuery(other) then other = luaq.iasQuery(other) end else luaq.util.errBadArgValue("overlap", 2, "查询对象", other, "(不是%s)。") end if sequence == nil then sequence = true elseif type(sequence) ~= "boolean" then luaq.util.errBadArgType("overlap", 4, "nil或boolean", sequence) end -- 获取两个结果表。 local result1 = query:distinct(func):query() local result2 = other:distinct(func):query() if #result1 == 0 or #result2 == 0 then return false end -- 任何一个为空集时两者不相交。 if sequence then for i = 1, 5 do local entry, seq if i == 1 then entry, seq = result1[1], result2 elseif i == 2 then entry, seq = result1[#result1], result2 elseif i == 3 then entry, seq = result2[1], result1 elseif i == 4 then etnry, seq = result2[#result2], result1 else return false -- 四种情况均未发现相等项,则两者不相交。 end for _, seqEntry in ipairs(seq) do local isEven if func then local evenTest = func(entry, seqEntry) -- 调用筛选函数。 isEven = evenTest == true or evenTest == 0 else isEven = luaq.util.equal(entry, seqEntry) end if isEven then return true end -- 发现相等项,两者相交。 end end else for index1, entry1 in ipairs(result1) do for index2, entry2 in ipairs(result2) do local isEven if func then local evenTest = func(entry1, entry2) -- 调用筛选函数。 isEven = evenTest == true or evenTest == 0 else isEven = luaq.util.equal(entry1, entry2) end if isEven then return true end -- 发现相等项,两者相交。 end end return false end end -- 查询对象的默认链接元函数。(必须提供concat、append、prepend函数的实现。) local function __concat(l, r) local v1, v2 = isQuery(l), isQuery(r) if v1 and v2 then return l:concat(r) elseif v1 and not v2 then return l:append(r) elseif not v1 and v2 then return l:prepend(r) else luaq.util.errBadArgValue("__concat", 1, "查询对象", l, "(不是%s)。") end end -- 查询对象的默认乘运算元函数。(必须提供join、duplicate函数的实现。) local function __mul(l, r) local v1, v2 = isQuery(l), isQuery(r) if v1 and v2 then return l:join(r) elseif v1 and type(v2) == "number" then return l:duplicate(r) elseif v2 and type(v1) == "number" then return r:duplicate(l) elseif v1 then luaq.util.errBadArgValue("__mul", 1, "查询对象", l, "(不是%s)。") else luaq.util.errBadArgType("__mul", 1, "number", l) end end -- 查询对象的默认商运算元函数。(必须提供groupBy函数的实现。) local function __div(query, key) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("__div", 1, "查询对象", query, "(不是%s)。") end if key == nil then luaq.util.errBadArgType("__div", 2, "非nil", key) end local func if type(key) == "function" then func = key else func = function(_, item) if type(item) == "table" then return item[key] else return nil end end end query:groupBy(func) end -- 查询对象的默认字符串元函数。 local function __tostring(query) if type(query) == "table" then if not isQuery(query) then query = luaq.iasQuery(query) end else luaq.util.errBadArgValue("__tostring", 1, "查询对象", query, "(不是%s)。") end return mw.dumpObject(query:query()) end function luaq.util.attachLuaq(q, mt) q.asQuery = luaq.asQuery q.select = luaq.select q.selectMany = luaq.selectMany q.selectYield = luaq.selectYield q.where = luaq.where q.count = luaq.count q.any = luaq.any q.all = luaq.all q.groupBy = luaq.groupBy q.orderBy = luaq.orderBy q.thenBy = luaq.thenBy q.distinct = luaq.distinct q.reverse = luaq.reverse q.concat = luaq.concat q.append = luaq.append q.prepend = luaq.prepend q.aggregate = luaq.aggregate q.reserve = luaq.reserve q.duplicate = luaq.duplicate --q.join = luaq.join --q.groupJoin = luaq.groupJoin q.first = luaq.first q.last = luaq.last q.only = luaq.only q.average = luaq.average q.max = luaq.max q.min = luaq.min q.sub = luaq.sub q.skip = luaq.skip q.take = luaq.take q.ofType = luaq.ofType q.query = luaq.query q.each = luaq.each q.has = luaq.has --q.union = luaq.union --q.except = luaq.except --q.symmetricExcept = luaq.symmetricExcept --q.intersect = luaq.intersect --q.complement = luaq.complement q.is = luaq.is q.subOf = luaq.subOf q.superOf = luaq.superOf q.properSubOf = luaq.properSubOf q.properSuperOf = luaq.properSuperOf q.overlap = luaq.overlap mt.__add = luaq.union mt.__sub = luaq.except mt.__mul = __mul mt.__div = __div mt.__unm = luaq.reverse mt.__mod = luaq.ofType mt.__power = luaq.join mt.__concat = __concat mt.__eq = luaq.is mt.lt = luaq.properSubOf mt.le = luaq.subOf mt.tostring = __tostring setmetatable(q, mt) end function luaq.test() local t = { a = 1, b = 5, c = 3, d = 4, e = 2, "a", "e", "c", "d", "b" } if (true) then mw.log("----------") mw.log("luaq.asQuery") for k, v in pairs(t) do mw.log(string.format("%s, %s", mw.dumpObject(k), mw.dumpObject(v))) end for i, k, v in enum(luaq.asQuery(t)) do mw.log(string.format("%d, %s, %s", i, mw.dumpObject(k), mw.dumpObject(v))) end mw.log("----------") mw.log("luaq.iasQuery") for k, v in ipairs(t) do mw.log(string.format("%d, %s", k, mw.dumpObject(v))) end for i, v in enum(luaq.iasQuery(t)) do mw.log(string.format("%d, %s", i, mw.dumpObject(v))) end mw.log("----------") mw.log("luaq.asQueryFrom") end local query1 = luaq.iasQuery({ 1, nil, "asdf", nil, "fdsa", nil, 2 }) local query2 = luaq.asQueryFrom(1, nil, "asdf", nil, "fdsa", nil, 2) if (true) then -- 只返回一个项。 for k, v in enum(query1) do mw.log(string.format("%d, %s", k, mw.dumpObject(v))) end -- 返回七个项。 for i, v in enum(query2) do mw.log(string.format("%d, %s", i, mw.dumpObject(v))) end mw.log("----------") mw.log("luaq.select") end local query3 = query2:select(function(i, v) mw.log(string.format("in select: %s, %s", mw.dumpObject(i), mw.dumpObject(v))) if i == 4 then return "string", nil elseif v == nil then return "nil", nil elseif type(v) == "string" then return "string", string.sub(v, 1, 1), string.sub(v, -1) else return type(v), v end end) if (true) then for i, vtype, v1, v2 in enum(query3) do if vtype == "string" then if v1 == nil then mw.log(string.format(" type: %s(explicit type), value: %s", vtype, mw.dumpObject(v1))) else mw.log(string.format(" type: %s, prefix: %s, suffix: %s", vtype, mw.dumpObject(v1), mw.dumpObject(v2))) end else mw.log(string.format(" type: %s, value: %s", vtype, mw.dumpObject(v1))) end end mw.log("----------") mw.log("luaq.where") end local query4 = query3:where(function(i, vtype, ...) return vtype ~= "nil" end) if (true) then for i, vtype, v1, v2 in enum(query4) do if vtype == "string" then if v1 == nil then mw.log(string.format(" type: %s(explicit type), value: %s", vtype, mw.dumpObject(v1))) else mw.log(string.format(" type: %s, prefix: %s, suffix: %s", vtype, mw.dumpObject(v1), mw.dumpObject(v2))) end else mw.log(string.format(" type: %s, value: %s", vtype, mw.dumpObject(v1))) end end mw.log("") resetstate(query4) for i, vtype, v1, v2 in enum(query4) do if vtype == "string" then if v1 == nil then mw.log(string.format(" type: %s(explicit type), value: %s", vtype, mw.dumpObject(v1))) else mw.log(string.format(" type: %s, prefix: %s, suffix: %s", vtype, mw.dumpObject(v1), mw.dumpObject(v2))) end else mw.log(string.format(" type: %s, value: %s", vtype, mw.dumpObject(v1))) end end end mw.log("----------") mw.log("luaq.empty") for _ in enum(luaq.empty()) do mw.log("in empty") end mw.log("----------") mw.log("luaq.count") mw.log(string.format("query4 has %d entries", query4:count())) mw.log("----------") mw.log("luaq.any") mw.log(string.format("any entry in luaq.empty? -%s", tostring(luaq.empty():any()))) mw.log(string.format("any number key in t? -%s\n or any string value equals \"f\"? -%s", tostring(luaq.asQuery(t):any(function(i, k) return type(k) == "number" end)), tostring(luaq.asQuery(t):any(function(i, v1, v2) return v1 == "f" end)) )) mw.log("----------") mw.log("luaq.all") mw.log(string.format("all entries in query2 are not function type? -%s", tostring(query2:all(function(i, v) return type(v) ~= "function" end)))) mw.log(string.format("all entries in query4 are not nil? -%s", tostring(query4:all(function(i, vtype, v) return v ~= nil end)))) mw.log("----------") mw.log("luaq.groupBy") mw.log("-- cache groups first --") local query5 = query2:groupBy( function(_, e) return type(e), nil, nil, nil, mw.ustring.len(type(e)) end -- 以类型来分组。 ) for gi, k, g in enum(query5) do mw.log(gi, unpack(k)) --mw.log(" count:", g:count()) end mw.log("-- cache entries first --") resetstate(query5) for gi, k, g in enum(query5) do mw.log(gi, unpack(k)) mw.log(" count:", g:count()) end mw.log("----------") mw.log("luaq.orderBy") mw.log("-- sort by type --") t = {} for i = 1, 20 do local e = math.random(1, 1000) if i % 2 == 1 then e = tostring(e) local r = math.random(1, 5) for _ = 1, r do e = "0" .. e end end table.insert(t, e) end mw.logObject(t) local query6 = luaq.iasQuery(t):orderBy( nil, function(le, re) le, re = type(le[1]), type(re[1]) if le < re then return -1 elseif le > re then return 1 else return 0 end end ) for i, k in enum(query6) do mw.log(" "..tostring(i), mw.dumpObject(k)) end mw.log("----------") mw.log("luaq.thenBy") mw.log("-- then sort by length of tostring in descending order --") local query7 = query6:thenBy( function(i, e) return mw.ustring.len(tostring(e)) end, nil, true -- desc ) for i, k in enum(query7) do mw.log(" "..tostring(i), mw.dumpObject(k)) end mw.log("-- then sort by tonumber --") local query8 = query7:thenBy( function(i, e) return tonumber(e) end ) for i, k in enum(query8) do mw.log(" "..tostring(i), mw.dumpObject(k)) end mw.log("----------") mw.log("luaq.distinct") local query9 = query8:distinct(function(a, b) return type(a[1]) == type(b[1]) end) mw.log(unpack(query9:query())) mw.log("----------") mw.log("luaq.each") query9:each(function(i, e) mw.log(i, mw.dumpObject(e)) end) mw.log("----------") mw.log("luaq.reverse") local query10 = query8:reverse() for i, k in enum(query10) do mw.log(" "..tostring(i), mw.dumpObject(k)) end mw.log("----------") mw.log("luaq.first luaq.last") mw.log(mw.dumpObject(query10:first()), mw.dumpObject(query10:last())) mw.log("----------") mw.log("luaq.ofType") local query11 = query10:ofType("number") mw.log(unpack(query11:query())) mw.log("----------") mw.log("luaq.max luaq.min") mw.log(mw.dumpObject(query11:max()), mw.dumpObject(query11:min())) mw.log("----------") mw.log("luaq.sub") local query12 = luaq.select({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, function(_, e) mw.log("","selecting", e) return e end) mw.log("-- sub(4, nil) --") for _, k in enum(query12:sub(4)) do mw.log("get", k) end resetstate(query12) mw.log("-- sub(4, 2) --") for _, k in enum(query12:sub(4, 2)) do mw.log("get", k) end resetstate(query12) mw.log("-- sub(4, 9) --") for _, k in enum(query12:sub(4, 9)) do mw.log("get", k) end resetstate(query12) mw.log("-- sub(4, -8) --") for _, k in enum(query12:sub(4, -8)) do mw.log("get", k) end resetstate(query12) mw.log("-- sub(4, -2) --") for _, k in enum(query12:sub(4, -2)) do mw.log("get", k) end resetstate(query12) mw.log("-- sub(-4, nil) --") for _, k in enum(query12:sub(-4)) do mw.log("get", k) end resetstate(query12) mw.log("-- sub(-4, 2) --") for _, k in enum(query12:sub(-4, 2)) do mw.log("get", k) end resetstate(query12) mw.log("-- sub(-4, 9) --") for _, k in enum(query12:sub(-4, 9)) do mw.log("get", k) end resetstate(query12) mw.log("-- sub(-4, -8) --") for _, k in enum(query12:sub(-4, -8)) do mw.log("get", k) end resetstate(query12) mw.log("-- sub(-4, -2) --") for _, k in enum(query12:sub(-4, -2)) do mw.log("get", k) end end return luaq