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
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] + } +}
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 {items:0..n,incremental {}} + + :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 +::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 +::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 ${: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 +::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 +::container3::member1 ::container3::member3 + +% container3 index +::container3::member3 ::container3::member1