[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: length operator # for tables with metatables
- From: "GrayFace" <sergroj@...>
- Date: Tue, 22 Sep 2009 05:24:16 +0700
Length operator ignores __index metamethod. This way it is fast and
predictable. It also ignores __len metamethod of tables and strings, again,
this makes it predictable and faster - for tables and strings it has a
definite meaning which cannot be altered. The same logic is used in other
operators.
Here is how # operator works, from the Manual:
function len_event (op)
if type(op) == "string" then
return strlen(op) -- primitive string length
elseif type(op) == "table" then
return #op -- primitive table length
else
local h = metatable(op).__len
if h then
-- call the handler with the operand
return (h(op))
else -- no handler available: default behavior
error(···)
end
end
end
----- Original Message -----
From: Leo Razoumov
To: Lua list
Sent: Tuesday, September 22, 2009 3:50 AM
Subject: length operator # for tables with metatables
Hi Everyone,
Lua manual defines length operator "#" for tables as follows:
"The length of a table t is defined to be any integer index n such
that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil, n
can be zero. For a regular array, with non-nil values from 1 to a
given n, its length is exactly that n, the index of its last value. If
the array has "holes" (that is, nil values between other non-nil
values), then #t can be any of the indices that directly precedes a
nil value (that is, it may consider any such nil value as the end of
the array)."
As one can see from the definition above tbl[#tbl+1] must always be nil.
Unfortunately, it is not the case when table has a metatable as the
following example illustrates:
a={11,22,33}
t= setmetatable({}, {__index=a})
print("#a = "..#a)
print("#t = "..#t)
print("t[#t+1]= "..t[#t+1])
The output:
#a = 3
#t = 0
t[#t+1]= 11
#t is zero while t[1], t[2] and t[3] are non-nil. Apparently, this
behavior contradicts just mentioned paragraph from the manual.
The situation can be made even more bizarre if one sets __newindex as well.
a={11,22,33}
t= setmetatable({}, {__index=a,__newindex=a})
print("#a = "..#a)
print("#t = "..#t)
t[#t+1] = "AA"
print("a[1] = ".. a[1])
The output:
#a = 3
#t = 0
a[1] = AA
Now, instead of appending a new table entry, t[#t+1] = "AA"
overrides the value of a[1].
Of course, I can always redefine __len meta-method to bring some
sanity to the table. What worries me, though, is that the default
behavior is so messy.
Am I missing something?
--Leo--