[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Conventions and classes, not new syntax (was: Re: Shorthand for appending to a table (was Re: 5.2 feature))
- From: "Paul Chiusano" <paul.chiusano@...>
- Date: Tue, 12 Sep 2006 12:35:44 -0400
I've found that a lot of desirable features can just be implemented in
stock Lua, just by creating classes and possibly adopting some
conventions. Why add a special syntax for table appends when you can
just implement the feature yourself in Lua, with the exact semantics
you want, in about 10 minutes?
If you need to track the number of entries in a table, you can just
write a class which does it. If you want tables to allow negative
indexing (to count back from end), just implement it as a class. If
you want a table which tracks the number of entries, just implement it
as a class. If you want the pairs function to work for objects that
you've constructed and not just raw tables, just overwrite the pairs
function to look for, say, a __pairs metamethod, etc. Usually, you can
have the syntax for whatever you add be identical to doing everything
with raw tables and with stock Lua (if that's what you want), the only
difference might be that when you construct, say, your souped-up table
which tracks the number of entries, you'll need to do Map {a=1, b=2}
instead of just {a=1,b=2}. Even that could be addressed with *very
simple* token filtering!
For instance, included is a List class whose metamethods allow you to
access the last element with t[Last], to append with t[End] = val, and
which also supports negative indexing. It just uses metamethods and a
simple convention to achieve the effect.
Here's an example of its use:
list = require 'List'
List, End, Last = list.List, list.End, list.Last
a = List(1,2,3,4,5) -- List is a vararg function that returns a List object
= a
List(1, 2, 3, 4, 5)
Negative indices count back from end:
= a[1], a[-1]
1 5
= a[-a.size()] -- -size gets first index
1
Assigning to nil deletes a slot.
a[-1] = nil
= a
List(1, 2, 3, 4)
a[-2] = nil
= a
List(1, 2, 4)
You can also get last index with special value 'Last':
= a[Last]
4
and you can append using a[End] = val
a[End] = "hello world!"
= a
List(1, 2, 4, hello world!)
End and Last aren't keywords, they're just unique values:
= End, Last
table: 0x304010 table: 0x303ae0
The metamethods just adopt the convention of treating these values as
special. 'Last' represents the last valid index in the List, and 'End'
is 'Last+1'. Since these values are unique to the List package,
there's no chance they'll accidentally be used someplace else.
And this is all just pure Lua! There's no special-purpose syntax, and
if you want your lists to have long meaningful names, you don't have
to type that name twice just to append. Of course, you might not want
these exact semantics (like maybe you don't want to do anything
special to negative indices, or maybe you want assigning to nil to do
something else), but these are easy to adapt.
Here's the code for List.lua. It uses the 'table of closures'
implementation of classes discussed in 16.4 of programming in Lua, but
it could probably be adapted to use the other way of implementing OO,
using the a:method() syntax.
-----
-- List.lua
-- These are just unique values to dispatch on
local End = {}
local Last = {}
local insert, remove = table.insert, table.remove
local function List(...)
-- this is the actual list upvalue
-- we could explicitly track the size, too, if we don't want to rely on '#'
local mElements = {...}
-- public interface table
local public = {}
-- this table is used to translate special indices: a[Last], a[End]
local dispatch = {
[End] = function() return #mElements+1 end,
[Last] = function() return #mElements end
}
local function translateNegative(ind)
return ind > 0 and ind or #mElements+ind+1
end
local function getIndex(v)
local tmp = dispatch[v]
return tmp and tmp() or translateNegative(v)
end
function public.add(val, index)
index = index or #mElements
insert(mElements, getIndex(index), val)
end
function public.remove(index)
index = index or #mElements
return remove(mElements, getIndex(index))
end
function public.get(index)
return mElements[getIndex(index or #mElements)]
end
function public.set(index, val)
if val==nil then return public.remove(index) end
mElements[getIndex(index)] = val
return public
end
function public.size()
return #mElements
end
function public.__tostring()
local buf = {}
for _,v in ipairs(mElements) do
buf[#buf+1] = tostring(v)
end
return "List("..table.concat(buf, ", ")..")"
end
public.__index = function(_,ind) return public.get(ind) end
public.__newindex = function(_,ind,val) return public.set(ind,val) end
setmetatable(public, public)
return public
end
return {List=List, End=End, Last=Last}