tuple
tuple
function tuple( ... ) --> tupleTable
Description
Constructs a tuple type, i.e. an object representing an unmodifiable list of fields.
Parameters
- …
-
List of tuple fields.
Return Values
- tubleTable
-
It returns a table that allows to read the tuple fields, but it forbit the modification of the fields. When called with same arguments, the same table reference will be returned. The unused reference will be automatically garbage collected.
Code
--ZFUNC-flyweightstore-v0 local function flyweightstore() --> ( ... ) --> reference local NIL, NAN = {}, {} local meta = { __index = function() error( "Can not access interned content directly.", 2 ) end, __newindex = function() error( "Can not change or add content to a flyweight store.", 2 ) end, } local internstore = setmetatable( {}, { __mode = "kv" } ) -- A map from child to parent is used to protect the internstore table's -- contents. -- In this way, they will he collected only when all the cildren are -- collected in turn. local parent = setmetatable( {}, { __mode = "k" } ) return function( ... ) local currentstore = internstore for a = 1, select( "#", ... ) do -- Get next intern field. Replace un-storable contents. local tonext = select( a, ... ) if tonext ~= tonext then tonext = NAN end if tonext == nil then tonext = NIL end -- Get or create the correspondent sub-intern local substore = rawget( currentstore, tonext ) if substore == nil then substore = setmetatable( {}, meta ) parent[ substore ] = currentstore rawset( currentstore, tonext, substore ) end currentstore = substore end return currentstore end end local tuplefact = flyweightstore() --ZFUNC-tuple-v0 local function tuple( ... ) --> tupleTable local tupleTable = tuplefact( ... ) if not getmetatable( tupleTable ).__type then -- First time initialization -- Store fields local fields = { ... } fields.n = select( "#", ... ) -- Dispatch to the stored fields, and forbid modification setmetatable( tupleTable, { type = "tuple", __index = function( t, k ) return fields[k] end, __newindex = function( t, k ) return error( "can not change tuple field", 2 ) end, } ) end return tupleTable end return tuple
Examples
local t = require "taptest" local tuple = require "tuple" local function diff( a, b ) return a ~= b end -- Equality operation t( type( tuple( 1, "a", true, 3 ) ), "table" ) t( tuple( 1, "a", true, 3 ), tuple( 1, "a", true, 3 ) ) -- Read fields local field = tuple( 1, "a", true, 3 ) t( field.n, 4 ) t( field[ 1 ], 1 ) t( field[ 2 ], "a" ) t( field[ 3 ], true ) t( field[ 4 ], 3 ) -- Store nil and NaN field = tuple( 1, nil, 0/0, 3 ) t( field.n, 4 ) t( field[ 1 ], 1 ) t( field[ 2 ], nil ) t( field[ 3 ], field[ 3 ], diff ) t( field[ 4 ], 3 ) -- Can not change field local a, b = pcall( function() tuple( 1, nil, 0/0, 3 )[ 1 ] = 2 end ) t( a, false ) t( b:match( "can not change tuple field" ), "can not change tuple field" ) -- Garbage collection test -- Check if the current lua version supports garbage collection metamethod local has_gc_meta setmetatable( {}, { __gc = function() has_gc_meta = true end } ) collectgarbage( "collect" ) local function skipon51( a, b ) if has_gc_meta then return a == b end return true, "skipped" end local gccount = 0 local x = tuple( 2, nil, 0/0, 4 ) x = setmetatable( x, { __gc = function( t ) gccount = gccount+1 end } ) -- No collection if some reference is still around collectgarbage( "collect" ) t( gccount, 0, skipon51 ) -- Automatic collection x = nil collectgarbage( "collect" ) t( gccount, 1, skipon51 ) t()