├── README.md ├── doc ├── .exclude ├── internal.gif └── readme.txt ├── multikey-scm-0.rockspec ├── src ├── multikey.lua └── multikey │ ├── memoize.lua │ └── tuple.lua └── tests ├── internal.test.lua ├── memoize.test.lua ├── multikey.test.lua └── tuple.test.lua /README.md: -------------------------------------------------------------------------------- 1 | lua-multikey 2 | ============ 3 | 4 | A simple multidimensional table 5 | 6 | See [here](http://siffiejoe.github.io/lua-multikey/). 7 | 8 | -------------------------------------------------------------------------------- /doc/.exclude: -------------------------------------------------------------------------------- 1 | ==> 2 | -------------------------------------------------------------------------------- /doc/internal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/siffiejoe/lua-multikey/7151c01f18af95a81f2e5b8b749dd50a73c91302/doc/internal.gif -------------------------------------------------------------------------------- /doc/readme.txt: -------------------------------------------------------------------------------- 1 | ![multikey Logo](multikey.png) 2 | 3 | # multikey -- A Lua Table Indexed by Multiple Keys # 4 | 5 | ## Introduction ## 6 | 7 | A Lua table maps single (non-nil) keys to single values. While it is 8 | easy to wrap multiple values in an array to store it in the table, 9 | using multiple keys as indices is not as simple because every array 10 | used to group multiple keys would map to a different value. 11 | This small module provides functions to store/retrieve values indexed 12 | by a key-tuple in a table. 13 | 14 | 15 | ## Getting Started ## 16 | 17 | Load the `multikey` module via `require` (you may want to assign 18 | a short variable name or add local aliases to the `put` and `get` 19 | functions). 20 | 21 | local mk = require( "multikey" ) 22 | local get, put = mk.get, mk.put 23 | 24 | You can use the module's functions on any table, previous values 25 | should be completely unaffected by this module (with the exception of 26 | the `clear` function). `get`, `put`, and `putv` are vararg functions 27 | that work with any number of keys, including zero. You can also use 28 | `nil` and `NaN` as keys. 29 | 30 | local t = { 1 } 31 | put( t, 1,2, "value1" ) 32 | put( t, 1,nil,3, "value2" ) 33 | put( t, 1,2,3, "value3" ) 34 | put( t, "value4" ) -- zero keys is possible as well 35 | print( get( t, 1,2 ) ) --> value1 36 | print( get( t, 1,nil,3 ) ) --> value2 37 | print( get( t, 1,2,3 ) ) --> value3 38 | print( get( t, 1 ) ) --> nil 39 | print( get( t ) ) --> value4 40 | print( t[ 1 ] ) --> 1 41 | 42 | `put` is convenient when you already know the keys you want to use. If 43 | you get the keys via a vararg list, the `putv` function is more 44 | useful, because it takes the value to assign as second parameter, 45 | before the keys. Assigning `nil` will remove a previous value stored 46 | under the same keys from the table. 47 | 48 | mk.putv( t, nil, 1,2,3 ) 49 | print( get( t, 1,2,3 ) ) --> nil 50 | 51 | For `pairs`- and `ipairs`-like iteration there are the `tuples` and 52 | `ituples` functions. Since `nil`s are valid keys, the `tuple` iterator 53 | produces an extra boolean as first generated value to avoid stopping 54 | the for loop prematurely. 55 | 56 | for _,a,b,c,d in mk.tuples( t ) do 57 | print( a, b, c, d ) 58 | end 59 | 60 | The `clear` function removes _all_ values from the table. 61 | In case you like an object-oriented syntax, the `new` function will 62 | return a table with a metatable set, so that you can use the module 63 | functions as methods. 64 | 65 | local t2 = mk.new() -- mk() would have the same effect 66 | t2:put( 1,2, "value" ) 67 | print( t2:get( 1,2 ) ) --> value 68 | t2:clear() 69 | print( t2:get( 1,2 ) ) --> nil 70 | 71 | That's basically it. 72 | 73 | 74 | ## Reference ## 75 | 76 | The following reference assumes that the result of `require`'ing the 77 | module is stored in a local variable `mk`. 78 | 79 | #### mk.new() or mk() #### 80 | 81 | mk.new() ==> object 82 | 83 | mk() ==> object 84 | 85 | Constructor for a multikey object that has a suitable metatable to 86 | allow calling all module functions as methods of this object. It also 87 | sets `__ipairs` and `__pairs` to the iterator functions below. 88 | 89 | #### mk.get() or obj:get() #### 90 | 91 | mk.get( table, ... ) ==> any -- the value 92 | ... : any* -- the keys to use for indexing 93 | 94 | The `get` function retrieves a value from a multikey table. The keys 95 | (zero or more) are given as separate arguments to this function and 96 | can be any Lua value (including `nil` and `NaN`). 97 | 98 | #### mk.put or obj:put() #### 99 | 100 | mk.put( table, ... ) ==> table -- returns the multikey table 101 | ... : any*, any -- the keys followed by a single value 102 | 103 | This function puts a value into the multikey table indexed by any 104 | number and type of keys. The last element of the vararg list is the 105 | value to be stored, all arguments before that are keys. 106 | 107 | #### mk.putv() or obj:putv() #### 108 | 109 | mk.putv( table, val, ... ) ==> table -- returns multikey table 110 | val : any -- the value to store 111 | ... : any* -- the keys to use for indexing 112 | 113 | This function works similar to the `put` function above, but the value 114 | to be stored comes before the keys in the argument list. This is 115 | useful if you get the keys via a vararg list yourself. 116 | 117 | #### mk.clear() or obj:clear() #### 118 | 119 | mk.clear( table ) ==> table -- returns the empty multikey table 120 | 121 | Sets any key/value in the given table to `nil`. 122 | 123 | #### mk.tuples() or obj:tuples() #### 124 | 125 | mk.tuples( table, ... ) ==> function -- for loop iterator 126 | ... : any* -- prefix keys where to start iteration from 127 | 128 | The `tuples` function returns a for loop iterator that iterates over 129 | all keys and values similar to the `pairs` iterator. Since `nil`s are 130 | valid keys in multikey tables, the first value the iterator returns is 131 | a non-`nil` dummy value. You can specify a set of keys to start the 132 | iteration from as arguments to this function. 133 | 134 | #### mk.ituples() or obj:ituples() #### 135 | 136 | mk.ituples( table, ... ) ==> function -- for loop iterator 137 | ... : any* -- prefix keys where to start iteration from 138 | 139 | The `ituples` function returns a for loop iterator that iterates over 140 | all integer keys starting from `1` (similar to `ipairs`). It does not 141 | use a dummy value as the `tuples` function above. 142 | 143 | 144 | ### How It Works ### 145 | 146 | This module exploits the fact that a Lua table can be used as a unique 147 | key in another table and as a container for other key-value mappings. 148 | Here is what a multikey object looks like after `t:put( "a", "a" )`, 149 | `t:put( "a","b", "a,b" )`, and `t:put( "a","c", "a,c" )`: 150 | 151 | t = { 152 | [ ] = { 153 | a = { -- this table is t1 154 | b = {}, -- this table is t2 155 | c = {} -- this table is t3 156 | } 157 | }, 158 | [ t1 ] = "a", -- table t1 from above is used as a key 159 | [ t2 ] = "a,b", -- table t2 from above is used as a key 160 | [ t3 ] = "a,c" -- table t3 from above is used as a key 161 | } 162 | 163 | This is the same thing but as a pretty picture: 164 | 165 | ![Internal Structure](internal.gif) 166 | 167 | 168 | ## multikey.memoize ## 169 | 170 | One popular use of normal Lua tables is to [memoize][1] functions, 171 | i.e. cache the result of a function call indexed by the argument of 172 | the call and reuse the result if the same arguments are used again. 173 | Normal tables only support one key and therefore single argument 174 | functions. Using the `multikey` module support for any number of 175 | arguments is possible. The `multikey.memoize` submodule contains such 176 | an implementation. 177 | 178 | [1]: http://lua-users.org/wiki/FuncTables 179 | 180 | Basic usage is: 181 | 182 | $ cat > test.lua 183 | local memoize = require( "multikey.memoize" ) 184 | local function f( a, b, c, d ) 185 | print( "f called:", a, b, c, d ) 186 | return a+b, c+d 187 | end 188 | local f2 = memoize( f ) 189 | print( f2( 1, 2, 3, 4 ) ) 190 | print( f2( 1, 2, 3, 4 ) ) 191 | print( f2( 1, 3, 3, 4 ) ) 192 | print( f2( 1, 3, 3, 4 ) ) 193 | print( f2( 1, 2, 3, 4 ) ) 194 | ^D 195 | 196 | The result is: 197 | 198 | $ lua test.lua 199 | f called: 1 2 3 4 200 | 3 7 201 | 3 7 202 | f called: 1 3 3 4 203 | 4 7 204 | 4 7 205 | 3 7 206 | 207 | Beware that this memoization is a lot more expensive than the single 208 | argument version, so profile first! 209 | 210 | 211 | ## Download ## 212 | 213 | The source code (with documentation and test scripts) is available on 214 | [github][2]. 215 | 216 | [2]: https://github.com/siffiejoe/lua-multikey/ 217 | 218 | 219 | ## Installation ## 220 | 221 | There are two ways to install this module, either using luarocks (if 222 | this module already is in the [main luarocks repository][3]) or 223 | manually. 224 | 225 | Using luarocks, simply type: 226 | 227 | luarocks install multikey 228 | 229 | To install the module manually just drop `multikey.lua` and the 230 | `multikey`-directory somewhere into your Lua `package.path`. 231 | 232 | [3]: http://luarocks.org/repositories/rocks/ (Main Repository) 233 | 234 | 235 | ## Contact ## 236 | 237 | Philipp Janda, siffiejoe(a)gmx.net 238 | 239 | Comments and feedback are always welcome. 240 | 241 | 242 | ## License ## 243 | 244 | `multikey` is *copyrighted free software* distributed under the MIT 245 | license (the same license as Lua 5.1). The full license text follows: 246 | 247 | multikey (c) 2011,2014 Philipp Janda 248 | 249 | Permission is hereby granted, free of charge, to any person obtaining 250 | a copy of this software and associated documentation files (the 251 | "Software"), to deal in the Software without restriction, including 252 | without limitation the rights to use, copy, modify, merge, publish, 253 | distribute, sublicense, and/or sell copies of the Software, and to 254 | permit persons to whom the Software is furnished to do so, subject to 255 | the following conditions: 256 | 257 | The above copyright notice and this permission notice shall be 258 | included in all copies or substantial portions of the Software. 259 | 260 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 261 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 262 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 263 | IN NO EVENT SHALL THE AUTHOR OR COPYRIGHT HOLDER BE LIABLE FOR ANY 264 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 265 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 266 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 267 | 268 | 269 | -------------------------------------------------------------------------------- /multikey-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "multikey" 2 | version = "scm-0" 3 | source = { 4 | url = "git://github.com/siffiejoe/lua-multikey.git" 5 | } 6 | description = { 7 | summary = "A simple multidimensional table.", 8 | detailed = [[ 9 | This small Lua module allows to store values indexed by 10 | multiple keys. 11 | ]], 12 | homepage = "http://siffiejoe.github.io/lua-multikey/", 13 | license = "MIT" 14 | } 15 | dependencies = { 16 | "lua >= 5.1, < 5.5" 17 | } 18 | build = { 19 | type = "builtin", 20 | modules = { 21 | [ "multikey" ] = "src/multikey.lua", 22 | [ "multikey.memoize" ] = "src/multikey/memoize.lua", 23 | [ "multikey.tuple" ] = "src/multikey/tuple.lua" 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/multikey.lua: -------------------------------------------------------------------------------- 1 | -- simple table adaptor for using multiple keys in a lookup table 2 | 3 | -- cache some global functions/tables for faster access 4 | local assert = assert 5 | local select = assert( select ) 6 | local next = assert( next ) 7 | local setmetatable = assert( setmetatable ) 8 | 9 | -- sentinel values for the key tree, nil keys, and nan keys 10 | local KEYS, NIL, NAN = {}, {}, {} 11 | 12 | 13 | local M = {} 14 | local M_meta = { 15 | __index = M 16 | } 17 | 18 | 19 | function M.new() 20 | return setmetatable( { [ KEYS ] = {} }, M_meta ) 21 | end 22 | setmetatable( M, { __call = M.new } ) 23 | 24 | 25 | function M.clear( t ) 26 | for k in next, t do 27 | t[ k ] = nil 28 | end 29 | return t 30 | end 31 | 32 | 33 | -- local helper function to map a vararg of keys to the real key 34 | local function get_key( key, ... ) 35 | for i = 1, select( '#', ... ) do 36 | if key == nil then break end 37 | local e = select( i, ... ) 38 | if e == nil then 39 | e = NIL 40 | elseif e ~= e then -- can only happen for NaNs 41 | e = NAN 42 | end 43 | key = key[ e ] 44 | end 45 | return key 46 | end 47 | 48 | 49 | function M.get( t, ... ) 50 | local key = get_key( t[ KEYS ], ... ) 51 | if key ~= nil then 52 | return t[ key ] 53 | end 54 | return nil 55 | end 56 | 57 | 58 | -- local helper function for both put variants below 59 | local function put( t, idx, val, n, ... ) 60 | for i = 1, n do 61 | local e = select( i, ... ) 62 | if e == nil then 63 | e = NIL 64 | elseif e ~= e then -- can only happen for NaNs 65 | e = NAN 66 | end 67 | local nextidx = idx[ e ] 68 | if not nextidx then 69 | nextidx = {} 70 | idx[ e ] = nextidx 71 | end 72 | idx = nextidx 73 | end 74 | t[ idx ] = val 75 | end 76 | 77 | 78 | -- returns true if tab can be removed from the parent table 79 | local function del( t, idx, n, ... ) 80 | if n > 0 then 81 | local e = ... 82 | if e == nil then 83 | e = NIL 84 | elseif e ~= e then -- can only happen for NaNs 85 | e = NAN 86 | end 87 | local nextidx = idx[ e ] 88 | if nextidx and del( t, nextidx, n-1, select( 2, ... ) ) then 89 | idx[ e ] = nil 90 | return t[ idx ] == nil and next( idx ) == nil 91 | end 92 | return false 93 | else 94 | t[ idx ] = nil 95 | return next( idx ) == nil 96 | end 97 | end 98 | 99 | 100 | function M.put( t, ... ) 101 | local n, keys, val = select( '#', ... ), t[ KEYS ], nil 102 | if n > 0 then 103 | val = select( n, ... ) 104 | n = n - 1 105 | end 106 | if val == nil then 107 | if keys ~= nil then 108 | del( t, keys, n, ... ) 109 | end 110 | else 111 | if keys == nil then 112 | keys = {} 113 | t[ KEYS ] = keys 114 | end 115 | put( t, keys, val, n, ... ) 116 | end 117 | return t 118 | end 119 | 120 | 121 | -- same as M.put, but value comes first not last 122 | function M.putv( t, val, ... ) 123 | local keys = t[ KEYS ] 124 | if val == nil then 125 | if keys ~= nil then 126 | del( t, keys, select( '#', ... ), ... ) 127 | end 128 | else 129 | if keys == nil then 130 | keys = {} 131 | t[ KEYS ] = keys 132 | end 133 | put( t, keys, val, select( '#', ... ), ... ) 134 | end 135 | return t 136 | end 137 | 138 | 139 | -- iteration is only available with coroutine support 140 | if coroutine ~= nil then 141 | local unpack = assert( unpack or table.unpack ) 142 | local pairs = assert( pairs ) 143 | local ipairs = assert( ipairs ) 144 | local co_yield = assert( coroutine.yield ) 145 | local co_wrap = assert( coroutine.wrap ) 146 | 147 | 148 | -- internal iterator function 149 | local function iterate( iter, t, key, keystack, n ) 150 | if t[ key ] ~= nil then 151 | keystack[ n+1 ] = t[ key ] 152 | co_yield( unpack( keystack, 1, n+1 ) ) 153 | end 154 | for k,v in iter( key ) do 155 | if k == NIL then 156 | k = nil 157 | elseif k == NAN then 158 | k = 0/0 159 | end 160 | keystack[ n+1 ] = k 161 | iterate( iter, t, v, keystack, n+1 ) 162 | end 163 | return nil 164 | end 165 | 166 | 167 | -- iterator similar to pairs, but since we have multiple keys ... 168 | function M.tuples( t, ... ) 169 | local vals, n = { true, ... }, select( '#', ... )+1 170 | return co_wrap( function() 171 | local key = get_key( t[ KEYS ], unpack( vals, 2, n ) ) 172 | if key ~= nil then 173 | return iterate( pairs, t, key, vals, n ) 174 | end 175 | end ) 176 | end 177 | 178 | 179 | function M.ituples( t, ... ) 180 | local vals, n = { ... }, select( '#', ... ) 181 | return co_wrap( function() 182 | local key = get_key( t[ KEYS ], unpack( vals, 1, n ) ) 183 | if key ~= nil then 184 | return iterate( ipairs, t, key, vals, n ) 185 | end 186 | end ) 187 | end 188 | 189 | 190 | -- Lua 5.2 metamethods for iteration 191 | M_meta.__pairs = M.tuples 192 | M_meta.__ipairs = M.ituples 193 | end 194 | 195 | 196 | -- return module table 197 | return M 198 | 199 | -------------------------------------------------------------------------------- /src/multikey/memoize.lua: -------------------------------------------------------------------------------- 1 | -- memoization with multiple arguments 2 | 3 | local assert = assert 4 | local select = assert( select ) 5 | local unpack = assert( unpack or table.unpack ) 6 | 7 | local multikey = require( "multikey" ) 8 | local get, putv = multikey.get, multikey.putv 9 | 10 | 11 | local function make_array( ... ) 12 | return { n = select( '#', ... ), ... } 13 | end 14 | 15 | 16 | local function memoize( func ) 17 | local store = {} 18 | return function( ... ) 19 | local res = get( store, ... ) 20 | if not res then 21 | res = make_array( func( ... ) ) 22 | putv( store, res, ... ) 23 | end 24 | return unpack( res, 1, res.n ) 25 | end, store 26 | end 27 | 28 | return memoize 29 | 30 | -------------------------------------------------------------------------------- /src/multikey/tuple.lua: -------------------------------------------------------------------------------- 1 | -- simple interning tuple implementation 2 | 3 | local assert = assert 4 | local next = assert( next ) 5 | local select = assert( select ) 6 | local error = assert( error ) 7 | local setmetatable = assert( setmetatable ) 8 | local getmetatable = assert( getmetatable ) 9 | local unpack = assert( unpack or table.unpack ) 10 | local newproxy = newproxy 11 | 12 | local multikey = require( "multikey" ) 13 | local get, putv = multikey.get, multikey.putv 14 | 15 | 16 | local cache = {} 17 | -- hack to create the internal data structures in the cache: 18 | putv( cache, 1, 1 ) 19 | putv( cache, nil, 1 ) 20 | -- hack to get a reference to the internal index data structure, 21 | -- to protect it, because we want to make the cache weak: 22 | local _, INDEX = next( cache ) 23 | setmetatable( cache, { __mode="v", INDEX = INDEX } ) 24 | 25 | 26 | local function make_array( ... ) 27 | return { n = select( '#', ... ), ... } 28 | end 29 | 30 | 31 | local function read_only( t ) 32 | error( "tuple is read-only data structure" ) 33 | end 34 | 35 | 36 | local function remove_self( t ) 37 | putv( cache, nil, unpack( getmetatable( t ).__index, 1, t.n ) ) 38 | end 39 | 40 | 41 | local function tuple( ... ) 42 | local v = get( cache, ... ) 43 | if not v then 44 | v = newproxy and newproxy( true ) 45 | or setmetatable( {}, { __gc = true } ) 46 | local m = getmetatable( v ) 47 | m.__index = make_array( ... ) 48 | m.__newindex = read_only 49 | m.__gc = remove_self 50 | putv( cache, v, ... ) 51 | end 52 | return v 53 | end 54 | 55 | return tuple 56 | 57 | -------------------------------------------------------------------------------- /tests/internal.test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | package.path = "../src/?.lua;../../microscope/src/?.lua;" .. package.path 4 | local multikey = require( "multikey" ) 5 | local microscope = require( "microscope" ) 6 | 7 | local mt = multikey() 8 | mt:put( "a", "a" ) 9 | mt:put( "a","b", "a,b" ) 10 | mt:put( "a","c", "a,c" ) 11 | 12 | microscope( "internal.dot", mt, "nometatables", "leaves" ) 13 | 14 | -------------------------------------------------------------------------------- /tests/memoize.test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | package.path = "../src/?.lua;" .. package.path 4 | local memoize = require( "multikey.memoize" ) 5 | local unpack = unpack or table.unpack 6 | 7 | local function args_table( ... ) 8 | return { n = select( '#', ... ), ... } 9 | end 10 | 11 | local function func( nr, a, b, c, d ) 12 | print( "test call nr.", nr ) 13 | return a, b, c+d 14 | end 15 | 16 | local args = { 17 | args_table( 1, nil, 2, 3 ), 18 | args_table( 1, 2, 3, 4 ), 19 | args_table( "x", {}, 4, 7 ), 20 | } 21 | 22 | local test = memoize( func ) 23 | 24 | local function dotest( nr ) 25 | print( "results = ", test( nr, unpack( args[ nr ], 1, args[ nr ].n ) ) ) 26 | end 27 | 28 | 29 | dotest( 1 ) 30 | dotest( 1 ) 31 | dotest( 2 ) 32 | dotest( 2 ) 33 | dotest( 2 ) 34 | dotest( 2 ) 35 | dotest( 3 ) 36 | dotest( 3 ) 37 | dotest( 3 ) 38 | 39 | -------------------------------------------------------------------------------- /tests/multikey.test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | package.path = "../src/?.lua;" .. package.path 4 | local mk = require( "multikey" ) 5 | local get, put, putv = mk.get, mk.put, mk.putv 6 | 7 | local t1 = {} 8 | 9 | 10 | assert( get( t1, 1,"x",3,4 ) == nil ) 11 | put( t1, 1,"x",3,4, "bla" ) 12 | assert( get( t1, 1,"x",3,4 ) == "bla" ) 13 | assert( get( t1, 1,"x",3 ) == nil ) 14 | putv( t1, "blub", 1,"x",3 ) 15 | assert( get( t1, 1,"x",3 ) == "blub" ) 16 | assert( get( t1, 1,"x",3,4 ) == "bla" ) 17 | put( t1, "val0" ) 18 | assert( get( t1 ) == "val0" ) 19 | put( t1, 1,"x",nil,4, "foo" ) 20 | assert( get( t1, 1,"x",nil,4 ) == "foo" ) 21 | put( t1, 1,"x",0/0,"a", "baz" ) 22 | assert( get( t1, 1,"x",0/0,"a" ) == "baz" ) 23 | put( t1, 1,1,1, 1 ) 24 | put( t1, 1,1,2, 2 ) 25 | put( t1, 1,2,1, 3 ) 26 | put( t1, 2, 4 ) 27 | 28 | local count = 0 29 | print( "pairs-like with 1,'x' prefix" ) 30 | for _, a, b, c, d, e in mk.tuples( t1, 1,"x" ) do 31 | count = count + 1 32 | assert( a == 1 and b == "x" ) 33 | print( a, b, c, d, e ) 34 | end 35 | assert( count == 4 ) 36 | 37 | count = 0 38 | print( "ipairs-like with 1,1 prefix" ) 39 | for a, b, c, d in mk.ituples( t1, 1,1 ) do 40 | count = count + 1 41 | assert( a == 1 and b == 1 and d == count ) 42 | print( a, b, c, d ) 43 | end 44 | assert( count == 2 ) 45 | 46 | put( t1 ) 47 | put( t1, 1,"x",3, nil ) 48 | assert( get( t1, 1,"x",3,4 ) == "bla" ) 49 | put( t1, 1,"x",3,4, nil ) 50 | put( t1, 1,"x",nil,4, nil ) 51 | put( t1, 1,"x",0/0,"a", nil ) 52 | put( t1, 1,1,1, nil ) 53 | put( t1, 1,1,2, nil ) 54 | put( t1, 1,2,1, nil ) 55 | put( t1, 2, nil ) 56 | 57 | count = 0 58 | for k,v in next, t1 do 59 | count = count + 1 60 | assert( type( k ) == "table" and type( v ) == "table" ) 61 | print( "t1:", k, v ) 62 | end 63 | assert( count == 1 ) 64 | 65 | t2 = mk.new() 66 | t2:put( 1, 1 ) 67 | assert( t2:get( 1 ) == 1 ) 68 | t2:clear() 69 | 70 | count = 0 71 | for k,v in next, t2 do 72 | count = count + 1 73 | assert( type( k ) == "table" and type( v ) == "table" ) 74 | print( "t2:", k, v ) 75 | end 76 | assert( count <= 1 ) 77 | 78 | t2:put( 1, 2, nil, nil, 2 ) 79 | t2:put( 1, 2, nil, nil, 3, nil ) 80 | t2:put( 1, 2, nil, nil, nil, nil ) 81 | assert( t2:get( 1,2,nil,nil ) == 2 ) 82 | assert( t2:get( 1,2,nil,nil,3 ) == nil ) 83 | assert( t2:get( 1,2,nil,nil,nil ) == nil ) 84 | t2:put( 1, 2, nil, nil, nil ) 85 | 86 | count = 0 87 | for k,v in next, t2 do 88 | count = count + 1 89 | assert( type( k ) == "table" and type( v ) == "table" ) 90 | print( "t2:", k, v ) 91 | end 92 | assert( count == 1 ) 93 | print( "ok" ) 94 | 95 | -------------------------------------------------------------------------------- /tests/tuple.test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | package.path = "../src/?.lua;" .. package.path 4 | local tuple = require( "multikey.tuple" ) 5 | 6 | 7 | do 8 | local t1 = tuple( 1, 2, 3 ) 9 | print( t1[ 1 ], t1[ 2 ], t1[ 3 ], t1.n ) 10 | collectgarbage() 11 | collectgarbage() 12 | local t2 = tuple( 1, 2, 3 ) 13 | print( t2[ 1 ], t2[ 2 ], t2[ 3 ], t2.n ) 14 | local t = { [ t1 ] = true } 15 | print( "ok?", t[ t2 ] ) 16 | print( "addresses:", t1, t2 ) 17 | end 18 | 19 | collectgarbage() 20 | collectgarbage() 21 | 22 | do 23 | local t1 = tuple( 1, 2, 3 ) 24 | print( "address:", t1 ) 25 | end 26 | 27 | --------------------------------------------------------------------------------