luaforwindows/files/lua/list.lua

378 lines
9.4 KiB
Lua

--- Tables as lists.
module ("list", package.seeall)
require "base"
require "table_ext"
--- An iterator over the elements of a list.
-- @param l list to iterate over
-- @return iterator function which returns successive elements of the list
-- @return the list <code>l</code> as above
-- @return <code>true</code>
function elems (l)
local n = 0
return function (l)
n = n + 1
if n <= #l then
return l[n]
end
end,
l, true
end
--- An iterator over the elements of a list, in reverse.
-- @param l list to iterate over
-- @return iterator function which returns precessive elements of the list
-- @return the list <code>l</code> as above
-- @return <code>true</code>
function relems (l)
local n = #l + 1
return function (l)
n = n - 1
if n > 0 then
return l[n]
end
end,
l, true
end
--- Map a function over a list.
-- @param f function
-- @param l list
-- @return result list <code>{f (l[1]), ..., f (l[#l])}</code>
function map (f, l)
return _G.map (f, elems, l)
end
--- Map a function over a list of lists.
-- @param f function
-- @param ls list of lists
-- @return result list <code>{f (unpack (ls[1]))), ..., f (unpack (ls[#ls]))}</code>
function mapWith (f, l)
return _G.map (compose (f, unpack), elems, l)
end
--- Filter a list according to a predicate.
-- @param p predicate (function of one argument returning a boolean)
-- @param l list of lists
-- @return result list containing elements <code>e</code> of
-- <code>l</code> for which <code>p (e)</code> is true
function filter (p, l)
return _G.filter (p, elems, l)
end
--- Return a slice of a list.
-- (Negative list indices count from the end of the list.)
-- @param l list
-- @param from start of slice (default: 1)
-- @param to end of slice (default: <code>#l</code>)
-- @return <code>{l[from], ..., l[to]}</code>
function slice (l, from, to)
local m = {}
local len = #l
from = from or 1
to = to or len
if from < 0 then
from = from + len + 1
end
if to < 0 then
to = to + len + 1
end
for i = from, to do
table.insert (m, l[i])
end
return m
end
--- Return a list with its first element removed.
-- @param l list
-- @return <code>{l[2], ..., l[#l]}</code>
function tail (l)
return slice (l, 2)
end
--- Fold a binary function through a list left associatively.
-- @param f function
-- @param e element to place in left-most position
-- @param l list
-- @return result
function foldl (f, e, l)
return _G.fold (f, e, elems, l)
end
--- Fold a binary function through a list right associatively.
-- @param f function
-- @param e element to place in right-most position
-- @param l list
-- @return result
function foldr (f, e, l)
return _G.fold (function (x, y) return f (y, x) end,
e, relems, l)
end
--- Prepend an item to a list.
-- @param l list
-- @param x item
-- @return <code>{x, unpack (l)}</code>
function cons (l, x)
return {x, unpack (l)}
end
--- Append an item to a list.
-- @param l list
-- @param x item
-- @return <code>{l[1], ..., l[#l], x}</code>
function append (l, x)
local r = {unpack (l)}
table.insert (r, x)
return r
end
--- Concatenate lists.
-- @param ... lists
-- @return <code>{l<sub>1</sub>[1], ...,
-- l<sub>1</sub>[#l<sub>1</sub>], ..., l<sub>n</sub>[1], ...,
-- l<sub>n</sub>[#l<sub>n</sub>]}</code>
function concat (...)
local r = {}
for _, l in ipairs ({...}) do
for _, v in ipairs (l) do
table.insert (r, v)
end
end
return r
end
--- Repeat a list.
-- @param l list
-- @param n number of times to repeat
-- @return <code>n</code> copies of <code>l</code> appended together
function rep (l, n)
local r = {}
for i = 1, n do
r = list.concat (r, l)
end
return r
end
--- Reverse a list.
-- @param l list
-- @return list <code>{l[#l], ..., l[1]}</code>
function reverse (l)
local m = {}
for i = #l, 1, -1 do
table.insert (m, l[i])
end
return m
end
--- Transpose a list of lists.
-- This function in Lua is equivalent to zip and unzip in more
-- strongly typed languages.
-- @param ls <code>{{l<sub>1,1</sub>, ..., l<sub>1,c</sub>}, ...,
-- {l<sub>r,1<sub>, ..., l<sub>r,c</sub>}}</code>
-- @return <code>{{l<sub>1,1</sub>, ..., l<sub>r,1</sub>}, ...,
-- {l<sub>1,c</sub>, ..., l<sub>r,c</sub>}}</code>
function transpose (ls)
local ms, len = {}, #ls
for i = 1, math.max (unpack (map (function (l) return #l end, ls))) do
ms[i] = {}
for j = 1, len do
ms[i][j] = ls[j][i]
end
end
return ms
end
--- Zip lists together with a function.
-- @param f function
-- @param ls list of lists
-- @return <code>{f (ls[1][1], ..., ls[#ls][1]), ..., f (ls[1][N], ..., ls[#ls][N])</code>
-- where <code>N = max {map (function (l) return #l end, ls)}</code>
function zipWith (f, ls)
return mapWith (f, transpose (ls))
end
--- Project a list of fields from a list of tables.
-- @param f field to project
-- @param l list of tables
-- @return list of <code>f</code> fields
function project (f, l)
return map (function (t) return t[f] end, l)
end
--- Turn a table into a list of pairs.
-- <br>FIXME: Find a better name.
-- @param t table <code>{i<sub>1</sub>=v<sub>1</sub>, ...,
-- i<sub>n</sub>=v<sub>n</sub>}</code>
-- @return list <code>{{i<sub>1</sub>, v<sub>1</sub>}, ...,
-- {i<sub>n</sub>, v<sub>n</sub>}}</code>
function enpair (t)
local ls = {}
for i, v in pairs (t) do
table.insert (ls, {i, v})
end
return ls
end
--- Turn a list of pairs into a table.
-- <br>FIXME: Find a better name.
-- @param ls list <code>{{i<sub>1</sub>, v<sub>1</sub>}, ...,
-- {i<sub>n</sub>, v<sub>n</sub>}}</code>
-- @return table <code>{i<sub>1</sub>=v<sub>1</sub>, ...,
-- i<sub>n</sub>=v<sub>n</sub>}</code>
function depair (ls)
local t = {}
for _, v in ipairs (ls) do
t[v[1]] = v[2]
end
return t
end
--- Flatten a list.
-- @param l list to flatten
-- @return flattened list
function flatten (l)
local m = {}
for _, v in ipairs (l) do
if type (v) == "table" then
m = concat (m, flatten (v))
else
table.insert (m, v)
end
end
return m
end
--- Shape a list according to a list of dimensions.
--
-- Dimensions are given outermost first and items from the original
-- list are distributed breadth first; there may be one 0 indicating
-- an indefinite number. Hence, <code>{0}</code> is a flat list,
-- <code>{1}</code> is a singleton, <code>{2, 0}</code> is a list of
-- two lists, and <code>{0, 2}</code> is a list of pairs.
-- <br>
-- Algorithm: turn shape into all positive numbers, calculating
-- the zero if necessary and making sure there is at most one;
-- recursively walk the shape, adding empty tables until the bottom
-- level is reached at which point add table items instead, using a
-- counter to walk the flattened original list.
-- <br>
-- @param s <code>{d<sub>1</sub>, ..., d<sub>n</sub>}</code>
-- @param l list to reshape
-- @return reshaped list
function shape (s, l)
l = flatten (l)
-- Check the shape and calculate the size of the zero, if any
local size = 1
local zero
for i, v in ipairs (s) do
if v == 0 then
if zero then -- bad shape: two zeros
return nil
else
zero = i
end
else
size = size * v
end
end
if zero then
s[zero] = math.ceil (#l / size)
end
local function fill (i, d)
if d > #s then
return l[i], i + 1
else
local t = {}
for j = 1, s[d] do
local e
e, i = fill (i, d + 1)
table.insert (t, e)
end
return t, i
end
end
return (fill (1, 1))
end
--- Make an index of a list of tables on a given field
-- @param f field
-- @param l list of tables <code>{t<sub>1</sub>, ...,
-- t<sub>n</sub>}</code>
-- @return index <code>{t<sub>1</sub>[f]=1, ...,
-- t<sub>n</sub>[f]=n}</code>
function indexKey (f, l)
local m = {}
for i, v in ipairs (l) do
local k = v[f]
if k then
m[k] = i
end
end
return m
end
--- Copy a list of tables, indexed on a given field
-- @param f field whose value should be used as index
-- @param l list of tables <code>{i<sub>1</sub>=t<sub>1</sub>, ...,
-- i<sub>n</sub>=t<sub>n</sub>}</code>
-- @return index <code>{t<sub>1</sub>[f]=t<sub>1</sub>, ...,
-- t<sub>n</sub>[f]=t<sub>n</sub>}</code>
function indexValue (f, l)
local m = {}
for i, v in ipairs (l) do
local k = v[f]
if k then
m[k] = v
end
end
return m
end
permuteOn = indexValue
--- Compare two lists element by element left-to-right
-- @param l first list
-- @param m second list
-- @return -1 if <code>l</code> is less than <code>m</code>, 0 if they
-- are the same, and 1 if <code>l</code> is greater than <code>m</code>
function compare (l, m)
for i = 1, math.min (#l, #m) do
if l[i] < m[i] then
return -1
elseif l[i] > m[i] then
return 1
end
end
if #l < #m then
return -1
elseif #l > #m then
return 1
end
return 0
end
-- Metamethods for lists
metatable = {
-- list .. table = list.concat
__concat = list.concat,
-- list == list retains its referential meaning
-- list < list = list.compare returns < 0
__lt = function (l, m) return compare (l, m) < 0 end,
-- list <= list = list.compare returns <= 0
__le = function (l, m) return compare (l, m) <= 0 end,
__append = list.append,
}
--- List constructor.
-- Needed in order to use metamethods.
-- @param t list (as a table)
-- @return list (with list metamethods)
function new (l)
return setmetatable (l, metatable)
end
-- Function forms of operators
_G.op[".."] = list.concat