This example is a small design study to implement container classes
with different features, namely a SimpleContainer
, an
OrderedContainer
and a SortedContainer
. First of all, we require NX:
package req nx nx::test configure -count 1
Simple Container
The first container class presented here is called
SimpleContainer
, which manages its contained items. As all
container classes presented here, the items are created as child
objects embedded in the container. If the container is deleted, all
items are deleted as well. The items, which will be put into the
container, should be instances of a certain class. We define here
for this purpose an arbitrary class C
:
nx::Class create C
The class SimpleContainer
keeps and manages items added to it.
Every instance of this class might have different item classes. We
might provide a prefix for naming the items, otherwise the default
is member
.
nx::Class create SimpleContainer { :property {memberClass ::MyItem} :property {prefix member} # Require the method "autoname" for generating nice names :require method autoname # The method new is responsible for creating a child of the current # container. :public method new {args} { set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args] return $item } }
Create and instance of the class SimpleContainer
…
% SimpleContainer create container1 -memberClass ::C
::container1
and add a few items:
% container1 new ::container1::member1 % container1 new ::container1::member2 % container1 new ::container1::member3
The elements of the container can be obtained via info children
:
% container1 info children
::container1::member1 ::container1::member2 ::container1::member3
Ordered Container
In the example with SimpleContainer
, the order of the results of
info children
just happens to be in the order of the added items,
but in general, this order is not guaranteed, but depends on the
population of the hash tables. In the next step, we extend the
example above by preserving the order of the elements.
The class OrderedContainer
is similar to SimpleContainer
, but
keeps a list of items that were added to the container. The item
list is managed in a property items
which is defined as
incremental
to make use of the add
and delete
methods provided
by the slots.
nx::Class create OrderedContainer -superclass SimpleContainer { :property -incremental {items:0..n {}} :public method new {args} { set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args] :items add $item end return $item } # Since we keep the list of items, we have to maintain it in case # items are deleted. :public method delete {item:object} { :items delete $item $item destroy } }
Create an instance of OrderedContainer
…
% OrderedContainer create container2 -memberClass ::C
::container2
and add a few items:
% container2 new ::container2::member1 % container2 new ::container2::member2 % container2 new ::container2::member3
The elements of the container are obtained via the method items
.
% container2 items get ::container2::member1 ::container2::member2 ::container2::member3
When we delete an item in the container …
% container2 delete ::container2::member2
the item is as well removed from the items
list.
% container2 items get ::container2::member1 ::container2::member3
Sorted Container
In the next step, we define a SortedContainer
, that keeps
additionally a sorted list for iterating through the items without
needing to sort the items when needed. The implementation maintains
an additional sorted list. The implementation of the SortedContainer
depends on "lsearch -bisect" which requires Tcl 8.6. Therefore, if
we have no Tcl 8.6, just return here.
if {[info command yield] eq ""} return
For sorting, we require the item class to have a key, that can be
freely specified. We use there the property name
of Class D
:
nx::Class create D { :property name:required } nx::Class create SortedContainer -superclass OrderedContainer { # In order to keep the index consisting of just the objects and to # ease sorting, we maintain two list, one list of values and one # list of objects. We assume for the time being, that the keys are # not changing. :variable values {} :variable index {} :property key :public method index {} { return ${:index}} :public method new {args} { set item [${:memberClass} create [:]::[:autoname ${:prefix}] {*}$args] if {[info exists :key]} { set value [$item cget -${:key}] set pos [lsearch -bisect ${:values} $value] set :values [linsert ${:values} [expr {$pos + 1}] $value] set :index [linsert ${:index} [expr {$pos + 1}] $item] } lappend :items $item return $item } # Since we keep the list of items, we have to maintain it in case # items are deleted. :public method delete {item:object} { set pos [lsearch ${:index} $item] if {$pos == -1} {error "item $item not found in container; items: ${:index}"} set :values [lreplace ${:values} $pos $pos] set :index [lreplace ${:index} $pos $pos] next } }
Create a container for class D
with key name
:
SortedContainer create container3 -memberClass ::D -key name
Add a few items
% container3 new -name victor ::container3::member1 % container3 new -name stefan ::container3::member2 % container3 new -name gustaf ::container3::member3
The method items
returns the items in the order of insertion (as before):
% container3 items get ::container3::member1 ::container3::member2 ::container3::member3
The method index
returns the items in sorting order (sorted by the name
member):
% container3 index ::container3::member3 ::container3::member2 ::container3::member1
Now we delete an item:
% container3 delete ::container3::member2
The item is as well removed from the result lists
% container3 items get ::container3::member1 ::container3::member3 % container3 index ::container3::member3 ::container3::member1