Recursive Data Types |
|
Say you want to represent a tree that has labelled nodes and numbered leaves.
To build such a data structure, you need a constructor for each variant of node: InteriorNode and LeafNode. And when traversing the data structure, you need a switch statement to do different things with the different data. The function deftype[3] or [4] creates a named class to help you do just that.
Here are the constructors[5] or [6]:
require'DefType.lua'
deftype'bintree'
function bintree:LeafNode( datum )
check( datum, 'number' )
return { datum }
end
function bintree:InteriorNode( t )
local name, left, right = unpack(t)
check( name, 'string' )
check( left, 'bintree' )
check( right, 'bintree' )
return t
end
bintree class that return a table. The settable event for bintree is used to "fix" the member functions to set the variant field of the table so that you can use the check function for type checking. The variant field is also used by the switch statement.
The tree in the diagram can now be defined as follows:
local b = bintree
local t = b:InteriorNode{
'cow',
b:InteriorNode{
'rat',
b:LeafNode( 1 ),
b:LeafNode( 2 ),
},
b:InteriorNode{
'pig',
b:InteriorNode{
'dog',
b:LeafNode( 3 ),
b:LeafNode( 4 ),
},
b:LeafNode( 5 ),
},
}
cases method is used to make the switch statement, which is just a table with a call event that calls the right function for each variant. Here is how you would sum the values of all the leaves in the tree.
local leafsum
leafsum = bintree:cases{
LeafNode = function( obj )
local datum = unpack(obj)
return datum
end,
InteriorNode = function( obj )
local name, left, right = unpack(obj)
return leafsum( left ) + leafsum( right )
end,
}
io.write( 'leaf sum = ', leafsum(t), '\n' )
local write = io.write
local strrep = string.rep
function indented( level, ... )
write( strrep( ' ', level ), unpack(arg) )
end
local treewalki
treewalki = bintree:cases{
LeafNode = function( obj, level )
local datum = unpack(obj)
write( ' @', obj.name, ' ', datum, '\n' )
end,
InteriorNode = function( obj, level )
local name, left, right = unpack(obj)
local lp1 = level+1
write( ' {\n' )
indented( lp1, '@', obj.name, ' ', name, '\n' )
indented( lp1, '@left' )
treewalki( left, lp1 )
indented( lp1, '@right' )
treewalki( right, lp1 )
indented( level, '}\n' )
end,
default = error,
}
function treewalk( tree )
write( '@Tree' )
treewalki(tree,0)
end
treewalk(t)
treewalk outputs:
@Tree {
@InteriorNode cow
@left {
@InteriorNode rat
@left @LeafNode 1
@right @LeafNode 2
}
@right {
@InteriorNode pig
@left {
@InteriorNode dog
@left @LeafNode 3
@right @LeafNode 4
}
@right @LeafNode 5
}
}
The DefType module make use of the Class module which can be found here
[7] or [8]