--[[ LUM 暂且解作 Lua Utilities Module, 即"Lua 实用模块". 本模块期望提供一些 Lua 和 Scribunto 没有提供, 但是对模块编写(特别是处理模板输入)有很大帮助的函数. 本模块期望能由多名对Lua较熟悉的用户一同维护; 目前的主要编者是 U:公的驱逐舰, 另获得了 U:Maya-Maja-Maia 的大力协助. 因为是16开始写的, 所以 Gotta praise the Crocc! 以下参与了模块编撰的作者已决定将他们在本模块的编辑通过 CC BY 4.0 释出: U:公的驱逐舰 U:Maya-Maja-Maia 仅包含以上作者的贡献的测试版本目前保留在中文萌娘百科的 Module:Sandbox/公的驱逐舰/LUM. 本模块的文档目前直接以注释的形式写在模块源代码内. 函数语法采用 func ( type, type, ... ) 或 func ( arg=type, arg1=type [ , arg2=type [ , arg3 = type ... ] ] ) 等方式记录, 其中, 等号左边是参数名、右边是函数期待的参数类型; 方括号内代表可选参数; 省略号代表可按格式重复的部分. "可使用命名参数表"的函数可只接受一个包含所有所需参数的 table 作为输入, 其 key 即为语法中定义的参数名称. "只可使用命名参数表"的函数即如其名. 不能使用命名参数表的简单函数不命名参数, 直接列出所需参数的类型. 推荐使用方法:在模块中加入 local lum = require('Module:LUM') 即可. ]] local lum = {} -------- 简单函数 Simple Functions -------- -- isEmpty ( string ). 返回 boolean. lum.isEmpty = function ( s ) return ( s == '' or s == nil ) end -- tableItemOrDefault ( table [ , variable [ , variable ] ] ). 返回值取决于 t, t 的子项或 default. lum.tableItemOrDefault = function ( t, default, key ) key = key or 1 return ( t and t[key] ) or default end lum.firstOrDefault = function ( t, default ) return lum.tableItemOrDefault ( t, default ) end -- for the lolz -------- 比较函数 Comparator -------- lum.comp = {} lum.comp.isAscending = function ( sign ) sign = (sign or ''):lower() return not ( sign == '>' or sign == '-' or sign == 'desc' or sign == 'd' ) end lum.comp.numThenString = function ( sign ) sign = lum.comp.isAscending ( sign ) return function ( a, b ) if type ( a ) == 'number' and type ( b ) == 'string' then return true elseif type ( b ) == 'number' and type ( a ) == 'string' then return false else return sign == ( a < b ) end end end lum.comp.nts = lum.comp.numThenString lum.comp.stringThenNum = function ( sign ) sign = lum.comp.isAscending ( sign ) return function ( a, b ) if type ( a ) == 'number' and type ( b ) == 'string' then return false elseif type ( b ) == 'number' and type ( a ) == 'string' then return true else return sign == ( a < b ) end end end lum.comp.stn = lum.comp.numThenString lum.comp.tableElement = function ( sign, key, default, comp ) sign = lum.comp.isAscending ( sign ) key = key or 1 default = default or '' comp = comp or function ( a, b ) return a < b end return function ( a, b ) return sign == comp ( ( a[key] or default ), ( b [key] or default ) ) end end lum.comp.te = lum.comp.tableElement lum.comp.tableElementOrSelf = function ( sign, key, default, comp ) sign = lum.comp.isAscending ( sign ) key = key or 1 default = default or '' comp = comp or function ( a, b ) return a < b end return function ( a, b ) return sign == comp ( ( a[key] or a or default ), ( b [key] or b or default ) ) end end lum.comp.teOrSelf = lum.comp.tableElementOrSelf -------- 函数协助 Function Helper -------- --[[ enablePackedArgs ( func=function, aliasNames=table [ , cond=function ] ); 可使用命名参数表; 别名 epa. 编写维护责任人: User:Maya-Maja-Maia. 返回一个 function. 本函数将不可使用命名参数表的输入函数 func 转化为可以使用命名参数表的函数并输出. aliasNames 应按序存放命名参数表中的参数名字. cond 接受函数的完整参数表作为输入, 判断是否应当将第一个参数视为命名参数表. 返回类型应当是 boolean. cond 未定义时, 默认为判断参数表是否只有一个参数. ]] lum.enablePackedArgs = function ( func, aliasNames, cond ) cond = cond or function ( args ) return #args == 1 end return function ( ... ) local packedArgs = {} if cond( arg ) then for ite = 1, #aliasNames do packedArgs[ite] = arg[1][aliasNames[ite]] end else packedArgs = arg end --mw.logObject ( packedArgs, 'epa: packedArgs' ) return func( unpack( packedArgs, 1, math.max(#packedArgs, #aliasNames) ) ) end end lum.enablePackedArgs = lum.enablePackedArgs( lum.enablePackedArgs, { 'func', 'aliasNames', 'cond' } ) lum.epa = lum.enablePackedArgs -------- 参数处理 Argument Processing -------- --[[ getArgFromAliases ( args=table, aliasTable=table [ , default=any [ , func=function ] ] ); 可使用命名参数表; 别名 gafa. 返回值类型取决于 args 和 default. 本函数按照 aliasTable 的顺序在 args 中寻找指定别名的参数, 并返回第一个 func 判断为 true 的参数本身; 如果用尽了 aliasTable 也没有符合 func 要求的参数, 则返回 default. default 未定义时默认为 nil. func 未定义时默认为 function ( a ) return a ~= nil end; 即未定义 func 时, 本函数会返回第一个不为 nil 的参数. func 会被按序提供两个输入:arg(类型取决于 args)和 alias(类型取决于 aliasTable); func 的返回类型应当是 boolean. 考虑到函数设计用途, func 应当有处理 nil 输入的能力. ]] lum.getArgFromAliases = function ( args, aliasTable, default, func ) func = func or function ( a ) return a ~= nil end for i=1, #aliasTable do if func ( args[ aliasTable[i] ], aliasTable[i] ) then return args[ aliasTable[i] ] end end return default end lum.getArgFromAliases = lum.epa( lum.getArgFromAliases, { 'args', 'aliasTable', 'default', 'func' } ) lum.gafa = lum.getArgFromAliases --[[ getNonEmptyArgFromAliases ( args=table, aliasTable=table, [default=any] ); 可使用命名参数表; 别名 gneafa. 返回值类型取决于 args 和 default. 本函数按照 aliasTable 的顺序在 args 中寻找指定别名的参数, 并返回第一个不为空字符串或 nil 的参数本身; 如果用尽了 aliasTable 也没有符合 func 要求的参数, 则返回 default. default 未定义时默认为 nil. ]] lum.getNonEmptyArgFromAliases = function ( args, aliasTable, default ) return lum.gafa ( args, aliasTable, default, function ( a ) return not lum.isEmpty( a ) end ) end lum.getNonEmptyArgFromAliases = lum.epa( lum.getNonEmptyArgFromAliases, { 'args', 'aliasTable', 'default' } ) lum.gneafa = lum.getNonEmptyArgFromAliases --[[ 很可惜, gabp还不能用. 迟些时候再修... getArgsByPattern ( args=table, ptn=string [ , keyMorph=function [ , comp=function ] ] ); 可使用命名参数表; 别名 gabp. 返回值为 table. 本函数尝试在 args 中寻找参数名称符合 ptn 定义的 ustring pattern 的参数, 并将其存入返回的 table 中. 返回值会是一个无名有序列表; 若本函数找不到符合的参数, 那么这个表将会是一个空表. 本函数会尝试使用 comp 函数为其排序, 方法如下: 找寻到的参数名(若 ptn 无 capture)/ ptn 的 capture(若有 capture)会被包装成一个无名有序 table 并被提供给 keyMorph; keyMorph 应当将输入转换成一个能够被 comp 接受的形式; comp 将对转换后的参数名/capture 排序, 然后使用这个排序来排序实际的参数数据. 转换后的参数名/capture 最终会被丢弃. comp 未定义时默认为 function ( a, b ) return a < b end; 若参数可以是乱序的, 可考虑将 comp 定义为 "function () return false end" 来最大化输入兼容性. keyMorph 未定义时默认为 function ( t ) return table.concat( t ) end. lum.getArgsByPattern = function ( args, ptn, comp, keyMorph ) comp = comp or function ( a, b ) return a < b end keyMorph = keyMorph or function ( t ) return table.concat( t ) end local p = function (...) local r = true if #arg > 2 then table.remove ( arg, 1 ) table.remove ( arg, 1 ) end return arg[1], ( r and arg or nil ) end local keys = {} local hold = {} local out = {} for k, v in pairs ( args ) do local found, keyTable = p ( mw.ustring.find ( k, ptn ) ) if found then key = keyMorph ( keyTable or { k } ) keys[#keys+1] = key hold[key] = v end end comp = comp or function ( a, b ) return a < b end table.sort ( keys, comp ) for i = 1, #hold do out[#out+1] = hold[ keys[i] ] end return out end lum.getArgsByPattern = lum.epa( lum.getArgsByPattern, { 'args', 'ptn', 'comp', 'keyMorph' } ) lum.gabp = lum.getArgsByPattern ]] --[[ parseArray ( str=string [ , flatten=boolean ] ); parseArray { 1=string [ , flatten=boolean ] }; 可使用命名参数表; 别名 parseTable. 返回值为 table, string, 或 nil. 本函数尝试将 str 分割成一个无名有序列表. str 的语法如下: ";"或者";" (全角) 是"一级分隔符"; "\"或者"、"是"二级分隔符". 每个一级项之间应用一级分隔符分隔, 如 "a;b;c" 或 "a\;b\;c\;" 会被分割为 { {"a"}, {"b"}, {"c"} }; 每个一级项内可包含若干二级项、以二级分隔符分隔, 如 "a1\a2;b;c1\c2\c3" 或 "a1\a2\;b\;c1\c2\c3\;" 会被分割为 { {"a1","a2"}, {"b"}, {"c1","c2","c3"} }; 当一个项位于字符串或一级项之末尾时, 可以省略末尾的对应分隔符. 本函数能够处理空项, 并会将其转换成空字符串; 但是, 空项在字符串或一级项之末尾, 且其之前有非空项时, 不得省略末尾的对应分隔符, 如 "a;;" 或 "a\;\;" 会被分割为 { {"a"}, {""} }; ";b1\\" 或 "\;b1\\;" 会被分割为 { {""}, {"b1",""} }; 本函数包含一个默认禁用的 flatten 功能. 启用时, 若每一个一级项内均只包含一个二级项, 那么一级项的 table 会被舍弃、替换成二级项本身; 若返回的 table 只包含一个一级项, 那么返回值会舍弃这个 table、直接返回一级项本身. 如: "a;b;c" 或 "a\;b\;c\;" 会被分割为 {"a","b","c"}; "1\2\3" 或 "1\2\3\;" 会被分割为 {"1","2","3"}; "one" 或 "one\;" 会被分割为 "one"; "a1\a2;b;c" 或 "a1\a2\;b\;c\;" 会被分割为 { {"a1","a2"}, {"b"}, {"c"} }. 作为特例, 当 str 为空字符串或 nil 时, 本函数会直接返回 str; 但是, "\", ";", 和 "\;" 会被正常分割为 {{""}} (禁用 flatten 时) 或 "" (启用 flatten 时). 请注意, 本模板不支持转义. 若输入中必须要包含以上四个分隔符之一, 请使用 HTML Character Entity (在萌娘百科可以使用 {{ce}} )转义. ]] lum.parseArray = function ( str, flatten, anonymousStr ) --mw.log ('parseArray: received data: '..table.concat( {tostring(str), tostring(flatten), tostring(anonymousStr)}, ', ') ) local s = str or anonymousStr if lum.isEmpty ( s ) then return s end flatten = flatten or false s = mw.ustring.gsub ( mw.ustring.gsub ( mw.ustring.gsub ( s, '、', '\\' ) , ';', ';' ), '([^;])$', '%1;' ) local out = {} local c1 = 0 local singleLeaf = true for m1 in mw.ustring.gmatch ( s, '([^;]*);') do c1 = c1 + 1 out[c1] = {} local c2 = 0 for m2 in mw.ustring.gmatch ( mw.ustring.gsub ( m1, '([^\\])$', '%1\\' ), '([^\\]*)\\') do c2 = c2 + 1 out[c1][c2] = m2 end if not out[c1][1] then out[c1][1] = '' end singleLeaf = singleLeaf and ( c2 < 2 ) end if flatten then if singleLeaf then for i = 1, #out do out[i] = out[i][1] end end if #out < 2 then out = out[1] end end return out end lum.parseArray = lum.epa( lum.parseArray, { 'str', 'flatten', 1 }, function ( a ) return type(a[1]) == 'table' end ) lum.parseTable = lum.parseArray -------- 未分类 Uncategorized -------- return lum