🐳chore(库):更新 lua penlight

develop
xiaojin 5 years ago
parent 5ad29c05af
commit 87e75eed73

@ -1,11 +1,17 @@
--- Operations on two-dimensional arrays. --- Operations on two-dimensional arrays.
-- See @{02-arrays.md.Operations_on_two_dimensional_tables|The Guide} -- See @{02-arrays.md.Operations_on_two_dimensional_tables|The Guide}
-- --
-- The size of the arrays is determined by using the length operator `#` hence
-- the module is not `nil` safe, and the usual precautions apply.
--
-- Note: all functions taking `i1,j1,i2,j2` as arguments will normalize the
-- arguments using `default_range`.
--
-- Dependencies: `pl.utils`, `pl.tablex`, `pl.types` -- Dependencies: `pl.utils`, `pl.tablex`, `pl.types`
-- @module pl.array2d -- @module pl.array2d
local tonumber,assert,tostring,io,ipairs,string,table = local tonumber,tostring,io,ipairs,string,table =
_G.tonumber,_G.assert,_G.tostring,_G.io,_G.ipairs,_G.string,_G.table _G.tonumber,_G.tostring,_G.io,_G.ipairs,_G.string,_G.table
local setmetatable,getmetatable = setmetatable,getmetatable local setmetatable,getmetatable = setmetatable,getmetatable
local tablex = require 'pl.tablex' local tablex = require 'pl.tablex'
@ -33,29 +39,48 @@ local function makelist (res)
return setmetatable(res, require('pl.List')) return setmetatable(res, require('pl.List'))
end end
--- return the row and column size.
-- Size is calculated using the Lua length operator #, so usual precautions
-- regarding `nil` values apply.
-- @array2d a a 2d array
-- @treturn int number of rows (`#a`)
-- @treturn int number of cols (`#a[1]`)
function array2d.size (a)
assert_arg(1,a,'table')
return #a,#a[1]
end
local function index (t,k) do
local function index (t,k)
return t[k] return t[k]
end end
--- return the row and column size. --- extract a column from the 2D array.
-- @array2d t a 2d array -- @array2d a 2d array
-- @treturn int number of rows -- @param j column index
-- @treturn int number of cols -- @return 1d array
function array2d.size (t) function array2d.column (a,j)
assert_arg(1,t,'table') assert_arg(1,a,'table')
return #t,#t[1] return makelist(imap(index,a,j))
end
end end
local column = array2d.column
--- extract a column from the 2D array. --- extract a row from the 2D array.
-- Added in line with `column`, for read-only purposes directly
-- accessing a[i] is more performant.
-- @array2d a 2d array -- @array2d a 2d array
-- @param key an index or key -- @param i row index
-- @return 1d array -- @return 1d array (copy of the row)
function array2d.column (a,key) function array2d.row(a,i)
assert_arg(1,a,'table') assert_arg(1,a,'table')
return makelist(imap(index,a,key)) local row = a[i]
local r = {}
for n,v in ipairs(row) do
r[n] = v
end
return makelist(r)
end end
local column = array2d.column
--- map a function over a 2D array --- map a function over a 2D array
-- @func f a function of at least one argument -- @func f a function of at least one argument
@ -63,7 +88,7 @@ local column = array2d.column
-- @param arg an optional extra argument to be passed to the function. -- @param arg an optional extra argument to be passed to the function.
-- @return 2d array -- @return 2d array
function array2d.map (f,a,arg) function array2d.map (f,a,arg)
assert_arg(1,a,'table') assert_arg(2,a,'table')
f = utils.function_arg(1,f) f = utils.function_arg(1,f)
return obj(a,imap(function(row) return imap(f,row,arg) end, a)) return obj(a,imap(function(row) return imap(f,row,arg) end, a))
end end
@ -101,8 +126,8 @@ end
--- map a function over two arrays. --- map a function over two arrays.
-- They can be both or either 2D arrays -- They can be both or either 2D arrays
-- @func f function of at least two arguments -- @func f function of at least two arguments
-- @int ad order of first array (1 or 2) -- @int ad order of first array (`1` if `a` is a list/array, `2` if it is a 2d array)
-- @int bd order of second array (1 or 2) -- @int bd order of second array (`1` if `b` is a list/array, `2` if it is a 2d array)
-- @tab a 1d or 2d array -- @tab a 1d or 2d array
-- @tab b 1d or 2d array -- @tab b 1d or 2d array
-- @param arg optional extra argument to pass to function -- @param arg optional extra argument to pass to function
@ -138,9 +163,9 @@ function array2d.product (f,t1,t2)
f = utils.function_arg(1,f) f = utils.function_arg(1,f)
assert_arg(2,t1,'table') assert_arg(2,t1,'table')
assert_arg(3,t2,'table') assert_arg(3,t2,'table')
local res, map = {}, tablex.map local res = {}
for i,v in ipairs(t2) do for i,v in ipairs(t2) do
res[i] = map(f,t1,v) res[i] = tmap(f,t1,v)
end end
return res return res
end end
@ -153,19 +178,21 @@ end
function array2d.flatten (t) function array2d.flatten (t)
local res = {} local res = {}
local k = 1 local k = 1
for _,a in ipairs(t) do -- for all rows local rows, cols = array2d.size(t)
for i = 1,#a do for r = 1, rows do
res[k] = a[i] local row = t[r]
for c = 1, cols do
res[k] = row[c]
k = k + 1 k = k + 1
end end
end end
return makelist(res) return makelist(res)
end end
--- reshape a 2D array. --- reshape a 2D array. Reshape the aray by specifying a new nr of rows.
-- @array2d t 2d array -- @array2d t 2d array
-- @int nrows new number of rows -- @int nrows new number of rows
-- @bool co column-order (Fortran-style) (default false) -- @bool co use column-order (Fortran-style) (default false)
-- @return a new 2d array -- @return a new 2d array
function array2d.reshape (t,nrows,co) function array2d.reshape (t,nrows,co)
local nr,nc = array2d.size(t) local nr,nc = array2d.size(t)
@ -195,30 +222,43 @@ function array2d.reshape (t,nrows,co)
return obj(t,res) return obj(t,res)
end end
--- transpose a 2D array.
-- @array2d t 2d array
-- @return a new 2d array
function array2d.transpose(t)
assert_arg(1,t,'table')
local _, c = array2d.size(t)
return array2d.reshape(t,c,true)
end
--- swap two rows of an array. --- swap two rows of an array.
-- @array2d t a 2d array -- @array2d t a 2d array
-- @int i1 a row index -- @int i1 a row index
-- @int i2 a row index -- @int i2 a row index
-- @return t (same, modified 2d array)
function array2d.swap_rows (t,i1,i2) function array2d.swap_rows (t,i1,i2)
assert_arg(1,t,'table') assert_arg(1,t,'table')
t[i1],t[i2] = t[i2],t[i1] t[i1],t[i2] = t[i2],t[i1]
return t
end end
--- swap two columns of an array. --- swap two columns of an array.
-- @array2d t a 2d array -- @array2d t a 2d array
-- @int j1 a column index -- @int j1 a column index
-- @int j2 a column index -- @int j2 a column index
-- @return t (same, modified 2d array)
function array2d.swap_cols (t,j1,j2) function array2d.swap_cols (t,j1,j2)
assert_arg(1,t,'table') assert_arg(1,t,'table')
for i = 1,#t do for _, row in ipairs(t) do
local row = t[i]
row[j1],row[j2] = row[j2],row[j1] row[j1],row[j2] = row[j2],row[j1]
end end
return t
end end
--- extract the specified rows. --- extract the specified rows.
-- @array2d t 2d array -- @array2d t 2d array
-- @tparam {int} ridx a table of row indices -- @tparam {int} ridx a table of row indices
-- @return a new 2d array with the extracted rows
function array2d.extract_rows (t,ridx) function array2d.extract_rows (t,ridx)
return obj(t,index_by(t,ridx)) return obj(t,index_by(t,ridx))
end end
@ -226,6 +266,7 @@ end
--- extract the specified columns. --- extract the specified columns.
-- @array2d t 2d array -- @array2d t 2d array
-- @tparam {int} cidx a table of column indices -- @tparam {int} cidx a table of column indices
-- @return a new 2d array with the extracted colums
function array2d.extract_cols (t,cidx) function array2d.extract_cols (t,cidx)
assert_arg(1,t,'table') assert_arg(1,t,'table')
local res = {} local res = {}
@ -251,29 +292,35 @@ function array2d.remove_col (t,j)
end end
end end
local function _parse (s) do
local c,r local function _parse (s)
if s:sub(1,1) == 'R' then local r, c = s:match 'R(%d+)C(%d+)'
r,c = s:match 'R(%d+)C(%d+)' if r then
r,c = tonumber(r),tonumber(c) r,c = tonumber(r),tonumber(c)
else
c,r = s:match '(.)(.)'
c = byte(c) - byte 'A' + 1
r = tonumber(r)
end
assert(c ~= nil and r ~= nil,'bad cell specifier: '..s)
return r,c return r,c
end end
c,r = s:match '(%a+)(%d+)'
if c then
local cv = 0
for i = 1, #c do
cv = cv * 26 + byte(c:sub(i,i)) - byte 'A' + 1
end
return tonumber(r), cv
end
error('bad cell specifier: '..s)
end
--- parse a spreadsheet range. --- parse a spreadsheet range.
-- The range can be specified either as 'A1:B2' or 'R1C1:R2C2'; -- The range can be specified either as 'A1:B2' or 'R1C1:R2C2';
-- a special case is a single element (e.g 'A1' or 'R1C1') -- a special case is a single element (e.g 'A1' or 'R1C1')
-- @string s a range. -- @string s a range (case insensitive).
-- @treturn int start col -- @treturn int start row
-- @treturn int start row -- @treturn int start col
-- @treturn int end col -- @treturn int end row
-- @treturn int end row -- @treturn int end col
function array2d.parse_range (s) function array2d.parse_range (s)
assert_arg(1,s,'string')
s = s:upper()
if s:find ':' then if s:find ':' then
local start,finish = splitv(s,':') local start,finish = splitv(s,':')
local i1,j1 = _parse(start) local i1,j1 = _parse(start)
@ -283,6 +330,7 @@ function array2d.parse_range (s)
local i,j = _parse(s) local i,j = _parse(s)
return i,j return i,j
end end
end
end end
--- get a slice of a 2D array using spreadsheet range notation. @see parse_range --- get a slice of a 2D array using spreadsheet range notation. @see parse_range
@ -293,21 +341,38 @@ end
-- @see array2d.slice -- @see array2d.slice
function array2d.range (t,rstr) function array2d.range (t,rstr)
assert_arg(1,t,'table') assert_arg(1,t,'table')
local i1,j1,i2,j2 = array2d.parse_range(rstr) return array2d.slice(t,array2d.parse_range(rstr))
if i2 then
return array2d.slice(t,i1,j1,i2,j2)
else -- single value
return t[i1][j1]
end
end end
local function default_range (t,i1,j1,i2,j2) local default_range do
local function norm_value(v, max)
if not v then return v end
if v < 0 then
v = max + v + 1
end
if v < 1 then v = 1 end
if v > max then v = max end
return v
end
--- normalizes coordinates to valid positive entries and defaults.
-- Negative indices will be counted from the end, too low, or too high
-- will be limited by the array sizes.
-- @array2d t a 2D array
-- @int i1 start row (default 1)
-- @int j1 start col (default 1)
-- @int i2 end row (default N)
-- @int j2 end col (default M)
-- return i1, j1, i2, j2
function array2d.default_range (t,i1,j1,i2,j2)
local nr, nc = array2d.size(t) local nr, nc = array2d.size(t)
i1,j1 = i1 or 1, j1 or 1 i1 = norm_value(i1 or 1, nr)
i2,j2 = i2 or nr, j2 or nc j1 = norm_value(j1 or 1, nc)
if i2 < 0 then i2 = nr + i2 + 1 end i2 = norm_value(i2 or nr, nr)
if j2 < 0 then j2 = nc + j2 + 1 end j2 = norm_value(j2 or nc, nc)
return i1,j1,i2,j2 return i1,j1,i2,j2
end
default_range = array2d.default_range
end end
--- get a slice of a 2D array. Note that if the specified range has --- get a slice of a 2D array. Note that if the specified range has
@ -341,7 +406,7 @@ end
--- set a specified range of an array to a value. --- set a specified range of an array to a value.
-- @array2d t a 2D array -- @array2d t a 2D array
-- @param value the value (may be a function) -- @param value the value (may be a function, called as `val(i,j)`)
-- @int i1 start row (default 1) -- @int i1 start row (default 1)
-- @int j1 start col (default 1) -- @int j1 start col (default 1)
-- @int i2 end row (default N) -- @int i2 end row (default N)
@ -349,8 +414,16 @@ end
-- @see tablex.set -- @see tablex.set
function array2d.set (t,value,i1,j1,i2,j2) function array2d.set (t,value,i1,j1,i2,j2)
i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2) i1,j1,i2,j2 = default_range(t,i1,j1,i2,j2)
for i = i1,i2 do local i = i1
if types.is_callable(value) then
local old_f = value
value = function(j)
return old_f(i,j)
end
end
while i <= i2 do
tset(t[i],value,j1,j2) tset(t[i],value,j1,j2)
i = i + 1
end end
end end
@ -379,8 +452,8 @@ end
--- perform an operation for all values in a 2D array. --- perform an operation for all values in a 2D array.
-- @array2d t 2D array -- @array2d t 2D array
-- @func row_op function to call on each value -- @func row_op function to call on each value; `row_op(row,j)`
-- @func end_row_op function to call at end of each row -- @func end_row_op function to call at end of each row; `end_row_op(i)`
-- @int i1 start row (default 1) -- @int i1 start row (default 1)
-- @int j1 start col (default 1) -- @int j1 start col (default 1)
-- @int i2 end row (default N) -- @int i2 end row (default N)
@ -424,27 +497,26 @@ end
--- iterate over all elements in a 2D array, with optional indices. --- iterate over all elements in a 2D array, with optional indices.
-- @array2d a 2D array -- @array2d a 2D array
-- @tparam {int} indices with indices (default false) -- @bool indices with indices (default false)
-- @int i1 start row (default 1) -- @int i1 start row (default 1)
-- @int j1 start col (default 1) -- @int j1 start col (default 1)
-- @int i2 end row (default N) -- @int i2 end row (default N)
-- @int j2 end col (default M) -- @int j2 end col (default M)
-- @return either value or i,j,value depending on indices -- @return either `value` or `i,j,value` depending on the value of `indices`
function array2d.iter (a,indices,i1,j1,i2,j2) function array2d.iter(a,indices,i1,j1,i2,j2)
assert_arg(1,a,'table') assert_arg(1,a,'table')
local norowset = not (i2 and j2)
i1,j1,i2,j2 = default_range(a,i1,j1,i2,j2) i1,j1,i2,j2 = default_range(a,i1,j1,i2,j2)
local i,j = i1-1,j1-1 local i,j = i1,j1-1
local row,nr = nil,0 local row = a[i]
local onr = j2 - j1 + 1
return function() return function()
j = j + 1 j = j + 1
if j > nr then if j > j2 then
j = j1 j = j1
i = i + 1 i = i + 1
if i > i2 then return nil end
row = a[i] row = a[i]
nr = norowset and #row or onr if i > i2 then
return nil
end
end end
if indices then if indices then
return i,j,row[j] return i,j,row[j]
@ -456,15 +528,31 @@ end
--- iterate over all columns. --- iterate over all columns.
-- @array2d a a 2D array -- @array2d a a 2D array
-- @return each column in turn -- @return column, column-index
function array2d.columns (a) function array2d.columns(a)
assert_arg(1,a,'table') assert_arg(1,a,'table')
local n = a[1][1] local n = #a[1]
local i = 0 local i = 0
return function() return function()
i = i + 1 i = i + 1
if i > n then return nil end if i > n then return nil end
return column(a,i) return column(a,i), i
end
end
--- iterate over all rows.
-- Returns a copy of the row, for read-only purrposes directly iterating
-- is more performant; `ipairs(a)`
-- @array2d a a 2D array
-- @return row, row-index
function array2d.rows(a)
assert_arg(1,a,'table')
local n = #a
local i = 0
return function()
i = i + 1
if i > n then return nil end
return array2d.row(a,i), i
end end
end end
@ -489,5 +577,3 @@ function array2d.new(rows,cols,val)
end end
return array2d return array2d

@ -33,10 +33,15 @@ local link_attrib = lfs.symlinkattributes
local path = {} local path = {}
local function err_func(name, param, err, code) local function err_func(name, param, err, code)
if code == nil then local ret = ("%s failed"):format(tostring(name))
return ("%s failed for '%s': %s"):format(tostring(name), tostring(param), tostring(err)) if param ~= nil then
ret = ret .. (" for '%s'"):format(tostring(param))
end end
return ("%s failed for '%s': %s (code %s)"):format(tostring(name), tostring(param), tostring(err), tostring(code)) ret = ret .. (": %s"):format(tostring(err))
if code ~= nil then
ret = ret .. (" (code %s)"):format(tostring(code))
end
return ret
end end
--- Lua iterator over the entries of a given directory. --- Lua iterator over the entries of a given directory.
@ -80,10 +85,10 @@ end
--- Get the working directory. --- Get the working directory.
-- Implicit link to [`luafilesystem.currentdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference) -- Implicit link to [`luafilesystem.currentdir`](https://keplerproject.github.io/luafilesystem/manual.html#reference)
-- @function currentdir -- @function currentdir
path.currentdir = function(d) path.currentdir = function()
local ok, err, code = currentdir(d) local ok, err, code = currentdir()
if not ok then if not ok then
return ok, err_func("currentdir", d, err, code), code return ok, err_func("currentdir", nil, err, code), code
end end
return ok, err, code return ok, err, code
end end

@ -379,7 +379,7 @@ local function _strip(s,left,right,chrs)
local rs = reverse(s) local rs = reverse(s)
local i1,i2 = find(rs, '^'..chrs..'*') local i1,i2 = find(rs, '^'..chrs..'*')
if i2 >= i1 then if i2 >= i1 then
t = -i2 t = -i2-1
end end
end end
end end

@ -62,7 +62,7 @@ end
-- An object is considered empty if it is: -- An object is considered empty if it is:
-- --
-- - `nil` -- - `nil`
-- - a table with out any items (key-value pairs or indexes) -- - a table without any items (key-value pairs or indexes)
-- - a string with no content ("") -- - a string with no content ("")
-- - not a nil/table/string -- - not a nil/table/string
-- @param o The object to check if it is empty. -- @param o The object to check if it is empty.

@ -18,7 +18,7 @@ local operators
local _function_factories = {} local _function_factories = {}
local utils = { _VERSION = "1.9.2" } local utils = { _VERSION = "1.11.0" }
for k, v in pairs(compat) do utils[k] = v end for k, v in pairs(compat) do utils[k] = v end
--- Some standard patterns --- Some standard patterns
@ -623,7 +623,11 @@ end
do do
-- the default implementation -- the default implementation
local deprecation_func = function(msg, trace) local deprecation_func = function(msg, trace)
if trace then
warn(msg, "\n", trace) -- luacheck: ignore warn(msg, "\n", trace) -- luacheck: ignore
else
warn(msg) -- luacheck: ignore
end
end end
--- Sets a deprecation warning function. --- Sets a deprecation warning function.
@ -634,12 +638,12 @@ do
-- function from the `compat` module for earlier versions). -- function from the `compat` module for earlier versions).
-- --
-- Note: only applications should set/change this function, libraries should not. -- Note: only applications should set/change this function, libraries should not.
-- @param func a callback with signature: `function(msg, trace)` both arguments are strings. -- @param func a callback with signature: `function(msg, trace)` both arguments are strings, the latter being optional.
-- @see utils.raise_deprecation -- @see utils.raise_deprecation
-- @usage -- @usage
-- -- write to the Nginx logs with OpenResty -- -- write to the Nginx logs with OpenResty
-- utils.set_deprecation_func(function(msg, trace) -- utils.set_deprecation_func(function(msg, trace)
-- ngx.log(ngx.WARN, msg, " ", trace) -- ngx.log(ngx.WARN, msg, (trace and (" " .. trace) or nil))
-- end) -- end)
-- --
-- -- disable deprecation warnings -- -- disable deprecation warnings
@ -668,9 +672,10 @@ do
-- function stringx.islower(str) -- function stringx.islower(str)
-- raise_deprecation { -- raise_deprecation {
-- source = "Penlight " .. utils._VERSION, -- optional -- source = "Penlight " .. utils._VERSION, -- optional
-- message = "function 'islower' was renamed to 'is_lower'" -- required -- message = "function 'islower' was renamed to 'is_lower'", -- required
-- version_removed = "2.0.0", -- optional -- version_removed = "2.0.0", -- optional
-- deprecated_after = "1.2.3", -- optional -- deprecated_after = "1.2.3", -- optional
-- no_trace = true, -- optional
-- } -- }
-- return stringx.is_lower(str) -- return stringx.is_lower(str)
-- end -- end
@ -680,7 +685,10 @@ do
if type(opts.message) ~= "string" then if type(opts.message) ~= "string" then
error("field 'message' of the options table must be a string", 2) error("field 'message' of the options table must be a string", 2)
end end
local trace = debug.traceback("", 2):match("[\n%s]*(.-)$") local trace
if not opts.no_trace then
trace = debug.traceback("", 2):match("[\n%s]*(.-)$")
end
local msg local msg
if opts.deprecated_after and opts.version_removed then if opts.deprecated_after and opts.version_removed then
msg = (" (deprecated after %s, scheduled for removal in %s)"):format( msg = (" (deprecated after %s, scheduled for removal in %s)"):format(

Loading…
Cancel
Save