Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- This file: -- http://anggtwu.net/LUA/Comprehensions1.lua.html -- http://anggtwu.net/LUA/Comprehensions1.lua -- (find-angg "LUA/Comprehensions1.lua") -- Author: Eduardo Ochs <eduardoochs@gmail.com> -- Date: 2025sep13 -- -- These classes can be used to show how set comprehensions are -- calculated step-by-step, using trees drawn as LaTeX matrices like -- the ones in: -- -- (mpgp 9 "comprehension-tables") -- (mpga "comprehension-tables") -- -- «.tocurly» (to "tocurly") -- «.tocurly-tests» (to "tocurly-tests") -- «.MTree» (to "MTree") -- «.MTree-tests-defs» (to "MTree-tests-defs") -- «.MTree-tests» (to "MTree-tests") -- «.Filter» (to "Filter") -- «.Comprehension» (to "Comprehension") -- «.Comprehension-tests» (to "Comprehension-tests") -- «tocurly» (to ".tocurly") tocurly_prefix = "" tocurly_prefix = "\\" tocurly = function (L, p) p = p or tocurly_prefix return p.."{"..mapconcat(tostring, L, ",")..p.."}" end -- «tocurly-tests» (to ".tocurly-tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "Comprehensions1.lua" = tocurly(seq(2,10,3)) = tocurly(seq(2,10,-1)) tocurly_prefix = "" = tocurly(seq(2,10,3)) --]] -- __ __ _____ -- | \/ |_ _| __ ___ ___ -- | |\/| | | || '__/ _ \/ _ \ -- | | | | | || | | __/ __/ -- |_| |_| |_||_| \___|\___| -- -- «MTree» (to ".MTree") -- Base on: (mpgp 9 "comprehension-tables") -- (mpga "comprehension-tables") -- (mpga "comprehension-tables" "Stop") -- -- MTrees are used to show how set comprehensions -- can be calculated using trees. -- The "M" means "LaTeX <M>atrix", -- and if m is an MTree then m:totex() -- generates the body of a LaTeX matrix, -- in a format like this: -- -- x & 6-x & \{x,...,6-x\} & y & (x,y) \\\hline -- 1 & 5 & \{1,2,3,4,5\} & 1 & (1,1) & -- & & & 2 & (1,2) & -- & & & 3 & (1,3) & -- & & & 4 & (1,4) & -- & & & 5 & (1,5) & -- 2 & 4 & \{2,3,4\} & 2 & (2,2) & -- & & & 3 & (2,3) & -- & & & 4 & (2,4) & -- 3 & 3 & \{3\} & 3 & (3,3) & -- 4 & 2 & \{\} & \Stop & -- 5 & 1 & \{\} & \Stop & -- MTree = Class { type = "MTree", new = function (o) return MTree {[0]=o} end, _ = function (o) return MTree {[0]=o} end, from = function (o) if type(o) == "number" then o = tostring(o) end if type(o) == "string" then return MTree {[0]=o} end if otype(o) == "MTree" then return o end -- Mtrees are returned unchanged local T = MTree(map(MTree.from, o)) -- other tables are converted recursively T[0] = o[0] -- keep the [0] field return T end, tosyntree = function (o) return MTree.from(o):tosyntree() end, totexrect = function (o) return MTree.from(o):totexrect() end, torect = function (o) return MTree.from(o):torect() end, totex = function (o) return MTree.from(o):totex() end, __tostring = function (o) return MTree.from(o):tostring() end, __index = { -- -- Generate the column names, as a Rect. colnames_syntree = function (T) return T:colnames_rect(" | ", "") end, colnames_tex = function (T) return T:colnames_rect(" & ", " \\\\\\HLine") end, colnames_rect = function (T,sep,post) if not T.columnnames then return Rect {} end local line = table.concat(T.columnnames, sep)..post return Rect {line} end, -- -- The default :tostring() method, that uses SynTrees. tostring = function (T) return T:torect():tostring() end, torect = function (T) return T:colnames_syntree() / T:tosyntree():torect() end, tosyntree = function (T) return SynTree.from(T) end, -- collect = function (T) return T:collectintoset() end, collectintoset = function (T) return Set.from(T:collectintolist()) end, collectintolist = function (T) return T:collectinto(VTable{}) end, collectinto = function (T,L) if #T==0 then if T[0] and T[0] ~= "\\Stop" then table.insert(L,T[0]) end else for _,subTree in ipairs(T) do subTree:collectinto(L) end end return L end, -- -- -- The :totex() method generates the body of a LaTeX matrix. -- The matrix is generated from left to right, recursively. -- Only the outer MTree can have a .columnnames field. totex = function (T) return T:totexrect():tostring() end, totexrect = function (T) return T:colnames_tex() / T:totexrect_LR() end, totexrect_R = function (T) local R = Rect {} for _,node in ipairs(T) do for _,line in ipairs(MTree.totexrect(node)) do table.insert(R, line) end end return R end, totexrect_L = function (T,topstr,height) local Ltop = format("%s & ", topstr) local Lblank = Ltop:gsub("[^&]", " ") return Ltop / Rect.rep(Lblank, height-1) end, totexrect_LR = function (T) local topstr = T[0] if #T==0 then return Rect {topstr.." \\\\"} end -- if we're on a leaf local R = T:totexrect_R() if not topstr then return R end -- if we're on the first generator return T:totexrect_L(topstr,#R)..R -- other cases end, -- -- Methods that add elements and subtrees to an MTree. -- These methods are very tricky! See MTree_test{1,2,3} below. add_ = function (T,o) local subTree = MTree.new(o) table.insert(T,subTree) return subTree end, addstop = function (T) T:add_("\\Stop") end, addfalse = function (T) T:add_("\\False"):addstop() end, addtrue = function (T) return T:add_("\\True") end, addstopifempty = function (T) if #T == 0 then T:addstop() end end, setcolumnnames = function (T,cols) T.columnnames = cols; return T end, }, } --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "Comprehensions1.lua" T = {{[0]=1}, {[0]=2, {[0]=23}, {[0]=24}}, {[0]=5}} = MTree.from(T) PPPV(MTree.from(T)) T = MTree.new() T:add_(1) do local T2 = T:add_(2) T2:add_(23) T2:add_(24) end T:add_(5) = T = T:totex() PPV(T) T.columnnames = {"x","y"} = T = T:totex() T = MTree.new() for x=1,2 do local T = T:add_(x) for y=3,4 do local T = T:add_(y) end end = T = T:totex() T.columnnames = {"x","y"} = T = T:totex() PPV(T) --]] -- __ __ _____ _ _ -- | \/ |_ _| __ ___ ___ | |_ ___ ___| |_ ___ -- | |\/| | | || '__/ _ \/ _ \ | __/ _ \/ __| __/ __| -- | | | | | || | | __/ __/ | || __/\__ \ |_\__ \ -- |_| |_| |_||_| \___|\___| \__\___||___/\__|___/ -- -- «MTree-tests-defs» (to ".MTree-tests-defs") -- These tests were hand-written, but the class `Comprehension', -- defined below, generates code similar to these examples. MTree_test0 = function () local T = MTree.new() for x=1,2 do for y=3,4 do for z=5,6 do T:add_(format("(%s,%s,%s)", x,y,z)) end end end return T end MTree_test1 = function () local T_ = MTree.new() for x=1,2 do local Tx = T:add_(x) for y=3,4 do local Ty = T:add_(y) for z=5,6 do local Tx = T:add_(z) end end end return T_ end -- { x in {1,...,5}, y in {x,...,6-x} ; (x,y) } -- \-/ -- lim -- \---------/ -- range -- MTree_test2 = function () local T = MTree._() for x=1,5 do local lim = 6-x local range = tocurly(seq(x,lim)) local T = T:add_(x) -- also called T local T = T:add_(lim) -- also called T local T = T:add_(range) -- also called T, etc for y=x,6-x do local T = T:add_(y) local T = T:add_(format("(%s,%s)",x,y)) end T:addstopifempty() end T.columnnames = {"x", "6-x", "\\{x,...,6-x\\}", "y", "(x,y)"} return T end -- «MTree-tests» (to ".MTree-tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "Comprehensions1.lua" = MTree_test0() = MTree_test0():totex() T3 = {[0]=3, 5, 6} T4 = {[0]=4, 5, 6} T1 = {[0]=1, T3, T4} T2 = {[0]=2, T3, T4} T0 = { T1, T2} T = MTree.from(T0) T.columnnames = {"x","y","z"} = T = MTree.from(T0) = MTree.totex(T0) = MTree.totex(T) T = MTree_test1() = T; = T:totex(); tocurly_prefix = "" T = MTree_test2() = T; = T:totex(); tocurly_prefix = "\\" T = MTree_test2() = T; = T:totex(); --]] -- «Filter» (to ".Filter") MTree_test3 = function () local T = MTree.new() for x=1,5 do local lim = 6-x local range = tocurly(seq(x,lim)) local T = T:add_(x) -- also called T local T = T:add_(lim) -- also called T local T = T:add_(range) -- also called T, etc for y=x,6-x do local T = T:add_(y) local T = T:add_(format("(%s,%s)",x,y)) end T:addstopifempty() end T.columnnames = {"x", "6-x", "\\{x,...,6-x\\}", "y", "(x,y)"} return T end --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "Comprehensions1.lua" = MTree_test3() --]] -- ____ _ _ -- / ___|___ _ __ ___ _ __ _ __ ___| |__ ___ _ __ ___(_) ___ _ __ -- | | / _ \| '_ ` _ \| '_ \| '__/ _ \ '_ \ / _ \ '_ \/ __| |/ _ \| '_ \ -- | |__| (_) | | | | | | |_) | | | __/ | | | __/ | | \__ \ | (_) | | | | -- \____\___/|_| |_| |_| .__/|_| \___|_| |_|\___|_| |_|___/_|\___/|_| |_| -- |_| -- -- «Comprehension» (to ".Comprehension") Comprehension = Class { type = "Comprehension", new = function () return Comprehension {prog = Rect{}} end, from = function (cmds) return Comprehension.new():runcommands(cmds):Function() end, __tostring = function (co) return co:tostring() end, __index = { tostring = function (co) return co:program() end, program = function (co) return co.prog:tostring() end, pre_ = function (co,r1) co.prog = torect(r1) / co.prog; return co end, post_ = function (co,r2) co.prog = co.prog / torect(r2); return co end, pre = function (co,fmt,...) return co:pre_ (format(fmt,...)) end, post = function (co,fmt,...) return co:post_(format(fmt,...)) end, -- var = function (co,str) -- co:var("_,x in seq(2,5)") returns "x" str = bitrim(str) str = str:gsub("^_,", "") str = str:gsub("^([0-9A-Za-z_]+).*", "%1") return str end, -- -- :Function() is the wrapping method that is executed last. -- It uses the internal methods :functionbody_(), that returns -- a Rect, and :columnnames_(), that returns a string or nil. columnnames_ = function (co) -- returns a string or nil if co.columnnames then local body = mapconcat(mytostring, co.columnnames, ", ") return format("T.columnnames = {%s}", body) end end, functionbody_ = function (co) -- returns a Rect return Rect {"local T = MTree.new()"} / Rect { co:columnnames_() } -- may be empty / co.prog / Rect {"return T"} end, Function = function (co) local indentedbody = " " .. co:functionbody_() co.prog = Rect {"function ()"} / indentedbody / Rect {" end"} return co end, -- Top = function (co,...) co.columnnames = {...}; return co end, -- For = function (co,gen) co.prog = Rect {format("for %s do", gen)} / (" " .. co.prog) / Rect {"end"} / Rect {"T:addstopifempty()"} return co end, Filt = function (co,expr) local indentedbody = " " .. (Rect {"local T = T:addtrue()"} / co.prog) co.prog = Rect {format("if %s then", expr)} / indentedbody / Rect {"else T = T:addfalse()"} / Rect {"end"} return co end, Column = function (co,expr) return co:pre("local T = T:add_(%s)", expr) end, Expr = function (co,expr) return co:pre("local T = T:add_(%s)", expr) end, Gen = function (co,gen) return co:Expr(co:var(gen)):For(gen) end, -- -- TODO: Expr should be Column + collect... -- -- Obsolete? Local = function (co,vardef,var) if var then co:Column(var) end return co:pre("local %s", vardef) end, -- -- splitcommand = function (co,line) return VTable(map(bitrim, split(line, "([^:]+)"))) end, runcommand = function (co,line) local parts = co:splitcommand(line) co[parts[1]](co, unpack(parts,2)) return co end, runcommands = function (co,lines) if type(lines) == "string" then lines = lines:gsub("|", "\n") lines = splitlines(lines) end for i=#lines,1,-1 do local line = lines[i] line = line:gsub("%-%-.*", "") line = bitrim(line) if line ~= "" then co:runcommand(line) end end return co end, eval = function (co) return expr(co:program())() end, }, } -- «Comprehension-tests» (to ".Comprehension-tests") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "Comprehensions1.lua" co = Comprehension.from [=[ Top:x :y :x+y :x+y<6 :10*x+y Gen:x=1,2 | Gen:y=3,4 | Expr:x+y | Filt:x+y<6 | Expr:10*x+y ]=] = co = co:eval() = co:eval():collectintolist() = co:eval():collectintoset() = co:eval():collectintoset():ksc(" ") = co:eval():collect():ksc(" ") = co:eval():totex() co = Comprehension.from [=[ Top:x :6-x :\{x,\ldots,6-x\} :y :(x,y) Gen:x=1,5 | Expr:6-x | Expr:tocurly(seq(x,6-x)) | Gen:y=x,6-x | Expr:format("(%s,%s)",x,y) ]=] = co = co:eval() = co:eval():collectintolist() = co:eval():collect():ksc(" ") = co:eval():totex() = Comprehension {prog=Rect{"BLA"}, columnnames={"x","y"}} = Comprehension {prog=Rect{"BLA"}, columnnames={"x","y"}} :Function() = Comprehension {prog=Rect{"BLA"}} :Function() = Comprehension {prog=Rect{"BLA"}} :Top ("x","y") :Function() co = Comprehension.new() = co:pre("a:%s","AA"):pre("b:%s","BB"):post("c:%s","CC"):post("d:%s","DD") -- Old: cmds = [=[ Colnames : x : 6-x : \{x,\ldots,6-x\} : y : (x,y) Gen : x = 1,5 Local : lim = 6-x : lim Local : range = tocurly(seq(x,lim)) : range Gen : y = x,6-x Expr : format("(%s,%s)",x,y) ]=] cmds = [=[ Colnames : x : 6-x : \{x,\ldots,6-x\} : y : (x,y) Gen : x = 1,5 --Expr : 6-x Expr : tocurly(seq(x,6-x)) Gen : y = x,6-x Expr : format("(%s,%s)",x,y) ]=] cmds = [=[ Gen : x = 1,4 Filt : x <= 2 Expr : x*10 ]=] co = Comprehension.from(cmds) = co = co:eval() = co:eval():totex() = co:eval():collect() co = Comprehension.from "Gen: x=1,2 | Gen: y=3,4" = co:eval() tocurly_prefix = "" tocurly_prefix = "\\" = MTree_test2() co = Comprehension.new() = co:Column ('format("(%s,%s)",x,y)') = co:For ('y=x,6-x', 'y') = co:Local ('range = tocurly(seq(x,lim))', 'range') = co:Local ('lim = 6-x', 'lim') = co:For ('x=1,5', 'x') = co:Function() co = Comprehension.new() = co:splitcommand "Local : range = tocurly(seq(x,lim)) : range" = co:runcommand "Local : range = tocurly(seq(x,lim)) : range" --]==] -- Local Variables: -- coding: utf-8-unix -- End: