lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


On 2017-07-03 21:04, J Doe wrote:
As a side question of that - how do people model a table or
array-type structure that has nils ?  Is a sentinel value like 0
used instead ?

One way is to have a field that stores the length.  (You can set a
metamethod to return that field instead of computing the length.)
Common names for that field are 'n', 'len', 'size', … – these do not
interfere with the array contents, as the array's keys are integers and
so you can clearly tell them apart from any metadata that you include
with e.g. string keys.

The simplest way to create an array with a "logical size" is

  table.pack( 5, 23, nil, 42, nil, nil )
  --> { [1] = 5, [2] = 23, [4] = 42, n = 6 }

and a simple(-ish) way to pack that up is

    local _iter = function( t, k )
       k = k + 1
       if k <= t.n then  return k, t[k]  end
    end

    Array = {
       fromValues = function( ... )
         return setmetatable( table.pack( ... ), Array )
       end,
       fromSize = function( n )
         return setmetatable( { n = n }, Array )
       end,
       __len = function( t )  return t.n  end,
       __pairs = function( t )  return _iter, t, 0  end,
    }

Behold:

    a = Array.fromValues( 5, 23, nil, 42, nil, nil )
    print( #a )
    --> 6
    for k, v in pairs( a ) do  print( k, v )  end
    --> 1  5
        2  23
        3  nil
        4  42
        5  nil
        6  nil

(If you want it to behave as if there are values everywhere, you can
additionally set

    Array.__index = function( t, k )
        k = tonumber( k )
        if k and 0 < k and k <= t.n then  return 0  end
    end

and you'll get

    for k, v in pairs( a ) do  print( k, v )  end
    --> 1  5
        2  23
        3  0
        4  42
        5  0
        6  0

while still getting

    a[-1], a[23]
    --> nil, nil

Likewise, you could use __newidex to ensure that no out-of-bounds writes can happen.)

A side effect of having __pairs is that metadata entries (outside of
what __pairs traverses) will be slightly harder to discover.  You can
access them directly if you know their key, or do a "raw pairs":

    for k, v in next, a, nil do  print( k, v )  end
    --> 1  5
        2  23
        4  42
        n  6

-- nobody