Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local lazy = {};
- local unpack = table.unpack or unpack
- -- Create a coroutine that delays evaluation of `x`.
- function lazy.delay(x, ...)
- local args = {...}
- return coroutine.create(function()
- if #args >= 1 then
- return x(unpack(args))
- end
- return x
- end)
- end
- -- Force-evaluate a coroutine.
- -- This function is strict in it's first argument, therefore
- -- force(bot) = bot
- -- Where bot is the bottom value, represented in Lua through
- -- a diverging call to `error`.
- --
- function lazy.force(x)--{{{
- if type(x) == 'thread' then
- if coroutine.status(x) ~= 'dead' then
- local ok, err = coroutine.resume(x)
- if not ok then
- error('Coroutine being force-evaluated diverged with error message: ' .. err, 0)
- else
- return err
- end
- else
- error('Can not force-evaluate dead coroutine: ' .. tostring(x), 0)
- end
- else
- return x
- end
- end--}}}
- -- Force-evaluate a coroutine, with a default value.
- -- This function, while strict in its first argument will not
- -- diverge if the passed coroutine diverges. Instead, the
- -- default argument will be used instead.
- function lazy.force_or(x, def)--{{{
- if type(x) == 'thread' then
- if coroutine.status(x) ~= 'dead' then
- local ok, err = coroutine.resume(x)
- if not ok then
- return def
- else
- return err
- end
- else
- return def
- end
- else
- return x
- end
- end--}}}
- function lazy.eval(...)--{{{
- return lazy.from_list({...}):reduce(function(x, y)
- lazy.force(x)
- return lazy.force(y)
- end):last()
- end--}}}
- function lazy.also(...) --{{{
- local args = {...}
- return function(x)
- local ret = x
- for k, v in pairs(args) do
- ret = v(ret)
- end
- return x
- end
- end --}}}
- lazy.Stream = {}
- local function mkstream(coro)--{{{
- assert(type(coro) == 'thread' or type(coro) == 'function', 'expected a coroutine or a function as first parameter of stream constructor')
- if type(coro) == 'function' then
- return setmetatable({thread = coroutine.create(coro)}, lazy.Stream)
- else
- return setmetatable({thread = coro}, lazy.Stream)
- end
- end--}}}
- setmetatable(lazy.Stream, { __call = mkstream })
- lazy.Stream.__call = mkstream
- lazy.Stream.__concat = lazy.Stream.concat
- lazy.Stream.__index = lazy.Stream;
- lazy.Stream.__type = 'stream'
- function lazy.Stream:__tostring()--{{{
- local x = {}
- local i = 1
- while self:has_next() do
- local e = self:next()
- if type(x) == 'string' then
- x[i] = ("%q"):format(e)
- else
- x[i] = tostring(e)
- end
- i = i + 1
- end
- return '[' .. table.concat(x, ', ') .. ']'
- end--}}}
- function lazy.Stream:print()--{{{
- io.write '['
- while self:has_next() do
- local x = self:next()
- if type(x) == 'string' then
- io.write(("'%s', ")
- :format(x))
- elseif type(x) == 'stream' then
- x:print()
- else
- io.write(("%s, ")
- :format(x))
- end
- end
- print ']'
- end--}}}
- function lazy.Stream:cons(a)--{{{
- assert(a, 'expected a value as the first argument of Stream:cons')
- return mkstream(function()
- coroutine.yield(a)
- while self:has_next() do
- coroutine.yield(self:next())
- end
- end)
- end--}}}
- function lazy.Stream:cons_mut(a) --{{{
- assert(a, 'expected a value as the first argument of Stream:cons')
- self.thread = coroutine.create(function()
- coroutine.yield(a)
- while self:has_next() do
- coroutine.yield(self:next())
- end
- end)
- end --}}}
- function lazy.Stream:snoc_mut(a) --{{{
- assert(a, 'expected a value as the first argument of Stream:cons')
- self.thread = coroutine.create(function()
- while self:has_next() do
- coroutine.yield(self:next())
- end
- coroutine.yield(a)
- end)
- end --}}}
- function lazy.Stream:snoc(a) --{{{
- assert(a, 'expected a value as the first argument of Stream:snoc')
- return mkstream(function()
- while self:has_next() do
- coroutine.yield(self:next())
- end
- coroutine.yield(a)
- end)
- end --}}}
- function lazy.Stream:has_next()--{{{
- if coroutine.status(self.thread) ~= 'dead' then
- local val = lazy.force_or(self.thread, nil)
- self.__cached_val = val
- return val ~= nil
- else
- return false
- end
- end--}}}
- function lazy.Stream:next()--{{{
- if self.__cached_val ~= nil then
- local val = self.__cached_val
- self.__cached_val = nil
- return val
- else
- return lazy.force_or(self.thread, nil)
- end
- end--}}}
- function lazy.Stream:peek() --{{{
- if self.__cached_val ~= nil then
- return self.__cached_val
- else
- self.__cached_val = lazy.force_or(self.thread, nil)
- return self.__cached_val
- end
- end --}}}
- function lazy.Stream:pull()--{{{
- if coroutine.status(self.thread) == 'dead' then
- return false
- else
- local ok, res = coroutine.resume(self.thread)
- if coroutine.status(self.thread) == 'dead' then
- return false
- elseif ok then
- return true, res
- else
- error(res)
- end
- end
- end--}}}
- function lazy.Stream:length()--{{{
- local i = 0
- while self:has_next() do
- i = i + 1
- self:next()
- end
- return i
- end--}}}
- function lazy.Stream:uncons()--{{{
- return self:next(), self
- end--}}}
- function lazy.Stream:unsnoc() --{{{
- return self:init(), self:last()
- end --}}}
- function lazy.Stream:head()--{{{
- return self:head()
- end--}}}
- function lazy.Stream:tail()--{{{
- return mkstream(function()
- self:next()
- while self:has_next() do
- coroutine.yield(self:next())
- end
- end)
- end--}}}
- function lazy.Stream:last()--{{{
- return self:fold(function(_, x) return x end, _.delay(error, "last: empty stream"))
- end--}}}
- function lazy.Stream:init()--{{{
- return mkstream(function()
- local ok, v1 = self:pull()
- while true do
- local ok, v2 = self:pull()
- if ok then
- coroutine.yield(v1)
- v1 = v2
- else
- break
- end
- end
- end)
- end--}}}
- function lazy.Stream:drop(n)--{{{
- assert(type(n) == 'number', 'expected the number of elements to drop as the first argument of Stream:drop')
- return mkstream(function()
- for i = 1, n do
- if self:has_next() then
- self:next()
- else
- return
- end
- end
- while self:has_next() do
- coroutine.yield(self:next())
- end
- end)
- end--}}}
- function lazy.Stream:take(n)--{{{
- assert(type(n) == 'number', 'expected the number of elements to take as the first argument of Stream:take')
- return mkstream(function()
- for i = 1, n do
- if self:has_next() then
- coroutine.yield(self:next())
- end
- end
- end)
- end--}}}
- function lazy.Stream:map(fun)--{{{
- assert(type(fun) == 'function', 'expected a function to map with as the first argument of Stream:map')
- return mkstream(function()
- while self:has_next() do
- local x = self:next()
- local x_ = fun(x)
- if x_ ~= nil then
- coroutine.yield(x_)
- else
- break
- end
- end
- end)
- end--}}}
- function lazy.Stream:filter(fun)--{{{
- assert(type(fun) == 'function', 'expected a predicate function as the first argument of Stream:filter')
- return mkstream(function()
- while self:has_next() do
- local x = self:next()
- if fun(x) then
- coroutine.yield(x)
- end
- end
- end)
- end--}}}
- function lazy.Stream:concat(b)--{{{
- assert(getmetatable(b) == lazy.Stream, 'expected a stream to concatenate with as the second argument (right-hand side) of Stream:map')
- return mkstream(function()
- while self:has_next() do
- coroutine.yield(lazy.force(self:next()))
- end
- while coroutine.status(b) ~= 'dead' do
- coroutine.yield(lazy.force(b))
- end
- end)
- end--}}}
- function lazy.Stream:zip(fun, ls, ...)--{{{
- assert(getmetatable(ls) == lazy.Stream, 'expected a stream as the second argument of Stream:zip')
- assert(type(fun) == 'function', 'expected a function as the first argument of Stream:zip')
- local args = {ls, ...}
- local function allAlive(args)
- for i = 1, #args do
- if not args[i]:has_next() then
- return false
- end
- end
- return true
- end
- local function nextAll(args)
- local ret = {}
- for i = 1, #args do
- ret[i] = args[i]:next()
- end
- return ret
- end
- return mkstream(function()
- while self:has_next() and allAlive(args) do
- coroutine.yield(fun(self:next(), unpack(nextAll(args))))
- end
- end)
- end--}}}
- function lazy.Stream:join(sep)--{{{
- assert(type(sep) == 'string', 'expected a string to use as the joiner')
- return self:fold(function(x, y)
- return x .. sep .. y
- end)
- end--}}}
- function lazy.Stream:fold(fn, init)--{{{
- assert(type(fn) == 'function', 'expected a function to fold with as the first argument of Stream:fold')
- local accum = init or self:next()
- while self:has_next() do
- accum = fn(accum, self:next())
- end
- return lazy.force(accum)
- end--}}}
- function lazy.Stream:flip() --{{{
- return self:fold(lazy.Stream.cons, lazy.null)
- end --}}}
- function lazy.Stream:flatten() --{{{
- return mkstream(function()
- while self:has_next() do
- local x = self:next()
- if getmetatable(x) == lazy.Stream then
- while x:has_next() do
- coroutine.yield(x:next())
- end
- else
- coroutine.yield(x)
- end
- end
- end)
- end --}}}
- function lazy.Stream:bind(f) --{{{
- return mkstream(function()
- while self:has_next() do
- local e = f(self:next())
- while e:has_next() do
- coroutine.yield(e:next())
- end
- end
- end)
- end --}}}
- lazy.Stream.flat_map = lazy.Stream.bind
- function lazy.Stream:intercalate(x)--{{{
- return mkstream(function()
- local ok, v1 = self:pull()
- while true do
- local ok, v2 = self:pull()
- if ok then
- coroutine.yield(v1)
- coroutine.yield(x)
- v1 = v2
- else
- coroutine.yield(v1)
- break
- end
- end
- end)
- end--}}}
- function lazy.Stream:collect()--{{{
- local i, c = {}, 1
- while self:has_next() do
- i[c] = self:next()
- c = c + 1
- end
- return i
- end--}}}
- function lazy.Stream:iterator() --{{{
- return function()
- while self:has_next() do
- return self:next()
- end
- end
- end
- --}}}
- function lazy.iterate(fn, x)--{{{
- assert(type(fn) == 'function', 'expected a generator function as the first argument of iterate')
- assert(x ~= nil, 'expected a seed')
- local ac = x
- return mkstream(function()
- while true do
- coroutine.yield(ac)
- ac = fn(ac)
- end
- end)
- end--}}}
- function lazy.seq(x, en, inc)--{{{
- assert(type(x) == 'number' or type(x) == 'BigNum', 'expected a number or a bignumber for the start value of seq')
- local i = x
- if not inc then
- inc = 1
- else
- inc = inc - x
- end
- return mkstream(function()
- while true do
- if (not en) or (en >= i) then
- coroutine.yield(i)
- i = i + inc
- else
- break
- end
- end
- end)
- end--}}}
- function lazy.unfold(gen, seed)--{{{
- assert(type(gen) == 'function', 'expected a generator function as the first argument to unfold')
- assert(seed, 'expected a seed value as the second argument to unfold')
- return mkstream(function()
- while true do
- local ok, val = gen(seed)
- if ok then
- seed = val
- coroutine.yield(ok)
- else
- break
- end
- end
- end)
- end--}}}
- function lazy.replicate(x, times)--{{{
- assert(x, 'expected a value to replicate')
- if times and type(times) == 'number' then
- return mkstream(function()
- for i = 1, times do
- coroutine.yield(x)
- end
- end)
- else
- return mkstream(function()
- while true do
- coroutine.yield(x)
- end
- end)
- end
- end --}}}
- function lazy.from_list(lis)--{{{
- assert(type(lis) == 'table', 'expected a list as the first argument to from_list')
- return mkstream(function()
- for i = 1, #lis do
- coroutine.yield(lis[i])
- end
- end)
- end--}}}
- function lazy.from_file(fil, mode, cnt)--{{{
- assert(type(fil) == 'string', 'expected a file path as the first argument to from_file')
- mode = mode or 'c'
- if mode == 'c' then
- return mkstream(function()
- local han = io.open(fil, 'r')
- if not han then
- error('file ' .. fil .. ' could not be opened for reading.')
- else
- local ch = han:read '*a'
- for i = 1, #ch do
- coroutine.yield(ch:sub(i,i))
- end
- han:close()
- end
- end)
- elseif mode == 'l' then
- return mkstream(function()
- local han = io.open(fil, 'r')
- if not han then
- error('file ' .. fil .. ' could not be opened for reading.')
- else
- for s in han:lines() do
- coroutine.yield(s)
- end
- han:close()
- end
- end)
- elseif mode == 'cb' and cnt then
- return mkstream(function()
- local han = io.open(fil, 'r')
- if not han then
- error('file ' .. fil .. ' could not be opened for reading.')
- else
- local all = han:read '*a'
- for i = 1, #all, cnt do
- coroutine.yield(all:sub(i, (i + cnt) - 1))
- end
- han:close()
- end
- end)
- elseif mode == 'w' then
- return mkstream(function()
- local han = io.open(fil, 'r')
- if not han then
- error('file ' .. fil .. ' could not be opened for reading.')
- else
- for s in han:lines() do
- for w in s:gmatch '[^ ]+' do
- coroutine.yield(w)
- end
- end
- han:close()
- end
- end)
- end
- end--}}}
- function lazy.stdin(mode) --{{{
- mode = mode or 'l'
- if mode == 'w' then
- return mkstream(function()
- local x = io.read '*l'
- while x ~= nil do
- for w in x:gmatch '[^ ]+' do
- coroutine.yield(w)
- end
- x = io.read '*l'
- end
- end)
- else
- return mkstream(function()
- local x = io.read '*l'
- while x ~= nil do
- coroutine.yield(x)
- x = io.read '*l'
- end
- end)
- end
- end --}}}
- function lazy.stdin_prompt(prompt, mode) --{{{
- mode = mode or 'l'
- if mode == 'w' then
- return mkstream(function()
- io.write(prompt)
- local x = io.read '*l'
- while x ~= nil do
- for w in x:gmatch '[^ ]+' do
- coroutine.yield(w)
- end
- io.write(prompt)
- x = io.read '*l'
- end
- end)
- else
- return mkstream(function()
- io.write(prompt)
- local x = io.read '*l'
- while x ~= nil do
- coroutine.yield(x)
- io.write(prompt)
- x = io.read '*l'
- end
- end)
- end
- end --}}}
- function lazy.words(x) --{{{
- return mkstream(function()
- for w in x:gmatch '[^ ]+' do
- coroutine.yield(w)
- end
- end)
- end --}}}
- lazy.null = mkstream(function() end)
- do --{{{ Install type() override
- local typ = type
- function type(x)
- if typ(x) == 'table' then
- if getmetatable(x) and getmetatable(x).__type then
- return getmetatable(x).__type
- else
- return 'table'
- end
- else
- return typ(x)
- end
- end
- lazy.type = type
- lazy._type = typ
- end --}}}
- return lazy
- -- vim: fdm=marker ts=2 et
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement