summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorheav <hheav3@gmail.com>2022-11-28 16:35:21 +0000
committerheav <hheav3@gmail.com>2022-11-28 16:35:21 +0000
commit9e01bb11a5af844980566d381db2bfa04f512324 (patch)
tree05638a0a2a3dcb42c453f3a71c0d42bd18fcee73
parentd9c2275a79bd090098e416861ec14d0294ae8c39 (diff)
made an iterator library.
-rw-r--r--czzc/init.lua6
-rw-r--r--czzc/iter.lua153
2 files changed, 157 insertions, 2 deletions
diff --git a/czzc/init.lua b/czzc/init.lua
index eea504b..04759b6 100644
--- a/czzc/init.lua
+++ b/czzc/init.lua
@@ -4,5 +4,7 @@ local function czzc_require(mod)
end
czzc_require"table"
-czzc_require"string"
-czzc_require"io"
+--czzc_require"string"
+--czzc_require"io"
+czzc_require"iter"
+czzc_require"math" \ No newline at end of file
diff --git a/czzc/iter.lua b/czzc/iter.lua
new file mode 100644
index 0000000..866b43a
--- /dev/null
+++ b/czzc/iter.lua
@@ -0,0 +1,153 @@
+local M = {}
+
+-- an important thing to keep in mind:
+-- factories often return three values, like ipairs.
+-- the actual function, an "invariant state", and a control variable.
+-- we'll just use closures to encapsulate this behaviour, but ipairs/pairs won't.
+-- as such, functions passing around iterators also ought to pass those around.
+
+-- the idea is that you can map a function over outputs of an iterator.
+-- for example, string.bytes over an iterator over characters of a string.
+-- returns an iterator.
+M.apply = function(fn, iter, ...)
+ return function()
+ local vals = {iter()}
+ if #vals > 0 then
+ -- me when lua versions.
+ return fn((unpack or table.unpack)(vals))
+ end
+ end, ...
+end
+
+-- apply, but only send n arguments of the iterator to the function.
+M.apply_n = function(args_n, fn, iter, ...)
+ return function()
+ local vals = {iter()}
+ if #vals > 0 then
+ local pt1 = {}
+ local pt2 = {}
+ for i=1, args_n do
+ table.insert(pt1, vals[i])
+ end
+ for i=args_n+1, #vals do
+ table.insert(pt2, vals[i])
+ end
+ return fn(table.unpack(pt1)), table.unpack(pt2)
+ end
+ end, ...
+end
+
+-- by factory, i mean a function generating iterators, like ipairs.
+-- this returns a factory.
+-- args_n is how many of the arguments to send to the function maximum, optionally.
+M.apply_factory = function(fn, fact, args_n)
+ if not args_n then
+ return function(...)
+ return M.apply(fn, fact(...))
+ end
+ else
+ return function(...)
+ return M.apply_n(args_n,fn, fact(...))
+ end
+ end
+end
+
+-- only works properly with iterators returning one value for now.
+-- returns an array of all of its results.
+M.collect = function(...)
+ local res = {}
+ for value, _ in ... do
+ table.insert(res, value)
+ end
+ return res
+end
+
+-- works with iterators of up to 8 values.
+-- there seems to be no way to make this generic, sorry.
+-- returns an array of arrays.
+M.multicollect = function(...)
+ local res = {}
+ for v1,v2,v3,v4,v5,v6,v7,v8 in ... do
+ table.insert(res, {v1,v2,v3,v4,v5,v6,v7,v8})
+ end
+ return res
+end
+
+-- gets an iterator's reverse.
+M.reverse = function(...)
+ local res = M.multicollect(...)
+ local i = #res
+ return function()
+ if i>0 then
+ i = i - 1
+ return table.unpack(res[i+1])
+ end
+ end
+end
+
+-- makes an iterator factory output reverses.
+M.reverse_factory = function(factory)
+ return function(...)
+ return M.reverse(factory(...))
+ end
+end
+
+-- alright, enough of these meta functions. time for some cool iterators.
+
+-- returns character, index.
+M.chars = function(str)
+ local i = 1
+ return function()
+ if i <= #str then
+ i = i + 1
+ return str:sub(i-1,i-1), i-1
+ end
+ end
+end
+
+-- returns all words, where words are defined as adjacent non-whitespace.
+-- of the form word, index.
+M.words = function(str)
+ local i = 0
+ local it = str:gmatch("%S+")
+ return function()
+ i = i + 1
+ return it(), i
+ end
+end
+
+-- cool, right?
+M.bytes = M.apply_factory(string.byte, M.chars, 1)
+
+-- iterates over the digits of a natural number starting with the
+-- least significant one. i.e 1003: 3 -> 0 -> 0 -> 1.
+-- returns digit index second, for if it's needed.
+M.digits = function(n)
+ local digit = 0
+ -- i think math.abs is the most reasonable way to handle negatives.
+ n = math.abs(n)
+ return function()
+ -- 0 is a special case since bee.
+ if n ~= 0 and n < 10^digit then return end
+ digit = digit + 1
+ return math.floor(n/10^(digit-1))%10, digit
+ end
+end
+-- the same as above, but from the most significant instead, i.e 1003: 1 -> 0 -> 0 -> 3.
+M.digits_msd = M.reverse_factory(M.digits)
+
+-- iterates over a table in reverse.
+M.ipairs_rev = M.reverse_factory(ipairs)
+
+-- autogenerate reversed variants of certain iterators.
+local to_be_reversed = {
+ "chars",
+ "bytes",
+ "words"
+}
+
+for _, v in ipairs(to_be_reversed) do
+ M[v.."_rev"] = M.reverse_factory(M[v])
+end
+
+return M \ No newline at end of file