Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
tuple/tuple.lua
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
183 lines (154 sloc)
3.82 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --n-tuple implementation based on an index tree. | |
| --Cosmin Apreutesei. Public domain. | |
| if not ... then require'tuple_test'; return end | |
| local weakvals_meta = {__mode = 'v'} | |
| --make a table with weak values. | |
| local function weakvals(t) | |
| return setmetatable(t or {}, weakvals_meta) | |
| end | |
| --make a table with strong values, i.e. make a table. | |
| local function strongvals(t) | |
| return t or {} | |
| end | |
| --convert nils and NaNs to be used as table keys. | |
| local NIL = {} | |
| local NAN = {} | |
| local function tokey(v) | |
| if v == nil then | |
| return NIL | |
| elseif v ~= v then | |
| return NAN | |
| else | |
| return v | |
| end | |
| end | |
| --make a new tuple space, with weak or strong references. | |
| --using strong references is faster but dead tuples won't get collected | |
| --until the space is released. | |
| local function space(weak, index, tuples) | |
| local weakvals = weak and weakvals or strongvals | |
| index = weakvals(index) --{k1 = index1}; index1 = {k2 = index2} | |
| tuples = weakvals(tuples) --{index1 = tuple(k1), index2 = tuple(k1, k2)} | |
| --find a matching tuple by going through the index tree. | |
| local function find(n, ...) | |
| local index = index | |
| for i = 1, n do | |
| local k = tokey(select(i, ...)) | |
| index = index[k] | |
| if not index then | |
| return | |
| end | |
| end | |
| return tuples[index] | |
| end | |
| --add a new tuple to the index tree. | |
| local function add(n, tuple) | |
| tuple.n = n | |
| local index = index | |
| for i = 1, n do | |
| local k = tokey(tuple[i]) | |
| local t = index[k] | |
| if not t then | |
| t = weakvals() | |
| index[k] = t | |
| end | |
| if weak and i < n then | |
| tuple[t] = true --anchor index table in the tuple. | |
| end | |
| index = t | |
| end | |
| tuples[index] = tuple | |
| return tuple | |
| end | |
| --get a matching tuple, or make a new one and add it to the index. | |
| local function tuple_vararg(...) | |
| local n = select('#', ...) | |
| return find(n, ...) or add(n, {...}) | |
| end | |
| --same as above, but for a fixed number of elements. | |
| local function tuple_narg(n, ...) | |
| local tuple = find(n, ...) | |
| if not tuple then | |
| tuple = {} | |
| for i = 1, n do | |
| tuple[i] = select(i, ...) | |
| end | |
| tuple = add(n, tuple) | |
| end | |
| return tuple | |
| end | |
| --same as above, but using a table as input. | |
| local function tuple_array(tuple) | |
| local n = tuple.n or #tuple | |
| return find(n, unpack(tuple, 1, n)) or add(n, tuple) | |
| end | |
| local function iterate() | |
| return coroutine.wrap(function() | |
| for _,v in pairs(tuples) do | |
| coroutine.yield(v) | |
| end | |
| end) | |
| end | |
| --remove an existing tuple, found by previously by creating it or from iteration. | |
| local function remove_from(index, t, i) | |
| local k = tokey(t[i]) | |
| local index1 = index[k] | |
| if index1 then | |
| return remove_from(index1, t, i+1) --tail call | |
| else | |
| tuples[index] = nil | |
| index[k] = nil | |
| end | |
| end | |
| local function remove(t) | |
| return remove_from(index, t, 1) | |
| end | |
| return tuple_vararg, tuple_narg, tuple_array, iterate, remove | |
| end | |
| --tuple class | |
| local tuple = {} | |
| local tuple_meta = {__index = tuple, __newindex = false} | |
| local function wrap(t) | |
| return setmetatable(t, tuple_meta) | |
| end | |
| function tuple:unpack(i, j) | |
| return unpack(self, i, self.n) | |
| end | |
| tuple_meta.__call = tuple.unpack | |
| function tuple_meta:__tostring() | |
| local t = {} | |
| for i=1,self.n do | |
| t[i] = tostring(self[i]) | |
| end | |
| return string.format('(%s)', table.concat(t, ', ')) | |
| end | |
| --integration with the pp module. | |
| function tuple_meta:__pwrite(write, write_value) | |
| write'tuple('; write_value(self[1]) | |
| for i=2,self.n do | |
| write','; write_value(self[i]) | |
| end | |
| write')' | |
| end | |
| --tuple space module | |
| local space_module | |
| function space_module(weak, ...) | |
| local tuple_vararg, tuple_narg, tuple_array, iterate, remove = space(weak, ...) | |
| return setmetatable({ | |
| space = space_module, | |
| narg = function(n, ...) | |
| return wrap(tuple_narg(n, ...)) | |
| end, | |
| from_array = function(t) | |
| return wrap(tuple_array(t)) | |
| end, | |
| tuples = iterate, | |
| remove = remove, | |
| }, { | |
| __call = function(_, ...) | |
| return wrap(tuple_vararg(...)) | |
| end | |
| }) | |
| end | |
| --default weak tuple space module | |
| return space_module(true) |