lua-users home
lua-l archive

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

On 28-Jun-07, at 2:31 PM, PA wrote:


I guess I'm still completely confused about how to deal with vararg, table and nil values...

(1) vararg can have nil values


(2) table behavior seems to be, errr, confusing when confronted with nil values

Yes, again.

local aTable = { nil, true }
print( #aTable )
> 2
-- Hmmm... two keys...

That use of # is not precisely defined, though. It could return 0.
Specifically, #t returns some i such that:
  t[i+1] == nil
  i == 0  or  t[i] ~= nil

Try aTable = { nil, true, nil }

print( table.maxn( aTable ) )
> 2
-- table.maxn agrees, two keys

maxn is the largest positive numeric key or 0
a = {[math.pi] = "pi"}; print(table.maxn(a))

print( table.getn( aTable ) )
> 2
-- Even table.getn seems to agree, two keys

getn is the same as # (and deprecated)

for aKey, aValue in pairs( aTable ) do
    print( aKey, aValue )
> 2       true
-- The pairs iterator seems to think there is only one key though

pairs() gives you all keys which don't map to nil

for aKey, aValue in ipairs( aTable ) do
    print( aKey, aValue )
-- And ipairs doesn't see any keys at all

ipairs iterates with integer keys starting at 1 until a key
is found which maps to nil.

This is all good and well, if slightly confusing... that said... how does one build a vararg at runtime considering all this?!?

One idiom is the following:

local args = {n=select('#', ...), ...}

for i = 1, args.n do --[[something]] end


There are a couple of things in your code:

    for anIndex = 1, select( '#', ... ) - 1 do

Why are you subtracting 1? Do you want to drop the last argument?

someArguments[ #someArguments + 1 ] = aValue

This is actually a useful idiom, but it doesn't do what you
think it does, I think.

Let's say that someArguments is a dense array; it has no embedded nils.
That's trivially true for someArguments = {}

Now, we apply induction:

If you do
  someArguments[#someArguments + 1] = "some non nil value"
then someArguments is still a dense array and #someArguments is
the number of non-nil elements.

If you do
  someArguments[#someArguments + 1] = nil
then someArguments is unchanged (since someArguments[#someArguments+1] was nil, by the definition of #). So someArguments is still a dense array, and #someArguments is the number of non-nil elements (i.e. it hasn't changed either.)

Consequently, at every step, someArguments is a dense array, and the
loop has the effect of collecting the non-nil values of aValue.

However, I gather that's not what you wanted; you wanted to map
the string "" to nil, without changing the indices of the following
elements. If you'd set things up as above, you could do this:

function replace_empty_with_nil(...)
  local args = {n=select("#", ...), ...}
  for i = 1, args.n do
    if args[i] == "" then args[i] = nil end
  return args

Note that the returned value has the 'n' key set to its length,
which can then be given to unpack:

someArguments = replace_empty_with_nil(f())
aHandler(unpack(someArguments, 1, someArguments.n))

If this seems like a reversion to Lua 4 and the old 'n'
field, so be it.... you could use a different key if you liked