cowan@marob.masa.com (John Cowan) (07/27/90)
" Purposes of COLL.ST: Support a whole bunch of new collection types. Requires BUGFIX.ST first. BitArray: efficiently stored Arrays whose elements must be 0 or 1. BooleanArray: efficiently stored Arrays whose elements must be true or false. IdentitySet: variant of Set where comparison criterion is ==, not =. NumericSet: efficiently stored Set whose elements must be members of a prespecified Interval. CharacterSet: efficiently stored Set whose elements must be Characters. CharacterInterval: variant of Interval ranging over Characters rather than numbers. Link: utility class for LinkedCollection. LinkedCollection: variant of OrderedCollection using a linked-list technology rather than an array technology (insertion and removal are fast, random access is very slow). Optionally, instances may also keep a Bag of their contents for fast lookup. LinkedSet: variant of LinkedCollection which does not allow duplicate elements. SortedLinkedCollection: variant of LinkedCollection which maintains elements in sorted order. SortedLinkedSet: combines virtues of LinkedSet and SortedLinkedCollection. RectArray: generalized n-dimensional arrays, allowing detailed specification of upper and lower bounds. Arrays are stored rectangularly in row-major order. Useful for converting matrix programs. SparseArray: efficiently stored Arrays most of whose elements should have the same value (need not be 0). Tree: data structure supporting a value, a parent link, and 0 to N child links, with methods for traversal. " Array variableSubclass: #BitArray instanceVariableNames: 'bytes size ' classVariableNames: 'Masks ' poolDictionaries: '' ! !BitArray class methods ! fromCharacter: aCharacter "Private - Answer 0 or 1 if aCharacter is $0 or $1. Otherwise signal an error." aCharacter == $0 ifTrue: [^0]. aCharacter == $1 ifTrue: [^1]. self error: 'Character must be 0 or 1.'! fromString: aString "Answer a new BitArray initialized by the characters in aString, which must be $0 or $1." | size answer | size := aString size. answer := self new: size. 1 to: size do: [:i | answer at: i put: (self fromCharacter: (aString at: i))]. ^answer! initialize "Private - Set up Masks class variable." Masks := #(1 2 4 8 16 32 64 128)! new "Answer an empty BitArray." ^self new: 0! new: anInteger "Answer a new BitArray with anInteger elements, all zeros." ^self basicNew initialize: anInteger! ! !BitArray methods ! asCharacter: aBit "Private - Answer aBit as a character." aBit == 1 ifTrue: [^$1] ifFalse: [^$0]! asString "Answers the receiver represented as a String of $0 and $1 characters." | answer | answer := String new: size. 1 to: size do: [:i | answer at: i put: (self asCharacter: (self at: i))]. ^answer! at: anInteger "Answers the bit at the index anInteger." | mask offset | self checkIndex: anInteger. mask := Masks at: ((anInteger - 1) \\ 8) + 1. offset := ((anInteger - 1) // 8) + 1. ((bytes at: offset) bitAnd: mask) ~= 0 ifTrue: [^1] ifFalse: [^0]! at: anInteger put: aBit "Stores aBit at element anInteger." | bit mask offset byte | self checkIndex: anInteger. bit := self bit: aBit. mask := Masks at: ((anInteger - 1) \\ 8) + 1. offset := ((anInteger - 1) // 8) + 1. byte := bytes at: offset. bit == 1 ifTrue: [byte := byte bitOr: mask] ifFalse: [byte := byte bitAnd: (mask bitXor: 255)]. bytes at: offset put: byte.! atAllPut: aBit "Fill every bit in the receiver with aBit." | value | (self bit: aBit) == 1 ifTrue: [value := 255] ifFalse: [value := 0]. 1 to: bytes size do: [:n | bytes at: n put: value]! bit: aBit "Signal an error if aBit is not 0 or 1." aBit == 0 ifTrue: [^0]. aBit == 1 ifTrue: [^1]. self error: 'A bit must be 0 or 1'! bitAnd: aBitArray "Answer a BitArray produced by AND-ing the bits of the receiver and aBitArray." | answer | ^self copy bitAndWith: aBitArray! bitAndWith: aBitArray "Modify the receiver by AND-ing its bits with those of aBitArray." self conformCheck: aBitArray. 1 to: bytes size do: [:n | bytes at: n put: ((bytes at: n) bitAnd: (aBitArray byteAt: n))]! bitOr: aBitArray "Answer a BitArray produced by OR-ing the bits of the receiver and aBitArray." ^self copy bitOrWith: aBitArray! bitOrWith: aBitArray "Modify the receiver by OR-ing its bits with those of aBitArray." self conformCheck: aBitArray. 1 to: bytes size do: [:n | bytes at: n put: ((bytes at: n) bitOr: (aBitArray byteAt: n))]! bitXor: aBitArray "Answer a BitArray produced by XOR-ing the bits of the receiver and aBitArray." ^self copy bitXorWith: aBitArray! bitXorWith: aBitArray "Modify the receiver by XOR-ing its bits with those of aBitArray." self conformCheck: aBitArray. 1 to: bytes size do: [:n | bytes at: n put: ((bytes at: n) bitXor: (aBitArray byteAt: n))]! byteAt: anInteger "Private - Answer a byte from the bytes array." ^bytes at: anInteger! conformCheck: aBitArray "Signal an error if aBitArray does not have the same size as the receiver." size = aBitArray size ifFalse: [self error: 'Bit arrays not conformable']! deepCopy "Since the contents are SmallIntegers, deepCopy == shallowCopy." ^self shallowCopy! flip "Invert the sense of every bit in the receiver." 1 to: bytes size do: [:n | bytes at: n put: ((bytes at: n) bitXor: 255)]! initialize: anInteger "Private - Initialize instance variables. Size of receiver will be anInteger." | byteCount | size := anInteger. byteCount := size // 8. (size \\ 8) == 0 ifFalse: [byteCount := byteCount + 1]. bytes := ByteArray new: byteCount.! not "Answer a BitArray with every bit in the receiver inverted." | answer | answer := self copy. ^answer flip! replace: aBitArray "Private - Replace our bits with aBitArray's bits." 1 to: bytes size do: [:n | bytes at: n put: (aBitArray byteAt: n)]! shallowCopy "Answer a copy of the receiver." ^(BitArray new: size) replace: self.! size "Answer the number of elements in the receiver." ^size! species "Species of BitArray is Array." ^Array! ! BitArray variableSubclass: #BooleanArray instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' ! !BooleanArray class methods ! fromCharacter: aCharacter "Private - Answer true or false if aCharacter is $T or $t or $F or $f. Otherwise signal an error." aCharacter == $T ifTrue: [^true]. aCharacter == $t ifTrue: [^true]. aCharacter == $F ifTrue: [^false]. aCharacter == $f ifTrue: [^false]. self error: 'Character must be T, t, F, or F.'! ! !BooleanArray methods ! asCharacter: aBoolean "Private - Answer a Boolean as $T or $F." aBoolean ifTrue: [^$T] ifFalse: [^$F]! at: anInteger "Answer the Boolean at the index anInteger." ^(super at: anInteger) == 1! bit: aBit "Answer 1 if aBit is true, 0 if aBit is false. Signal an error otherwise." aBit ifTrue: [^1] ifFalse: [^0]! ! Set subclass: #IdentitySet instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' ! !IdentitySet class methods ! ! !IdentitySet methods ! findElementIndex: anObject "Private - Answer the index position of anObject in the receiver or the first empty element position. Copied from Set method, but with = changed to ==." | index indexedObject lastIndex | lastIndex := contents size. index := (anObject hash) \\ lastIndex + 1. [(indexedObject := contents at: index) == anObject] whileFalse: [ (indexedObject == nil) ifTrue: [^index]. (index := index + 1) > lastIndex ifTrue: [ "index wraparound" index := 1]]. ^index! ! Set subclass: #NumericSet instanceVariableNames: 'interval ' classVariableNames: '' poolDictionaries: '' ! !NumericSet class methods ! new "Must use over: to create an NumericSet." ^self invalidMessage! new: anInteger "Must use over: to create an NumericSet." ^self invalidMessage! over: anInterval "Answer a new empty NumericSet defined over anInterval. If anInterval is really a number, use 1 to: anInterval instead." | interval | (anInterval isKindOf: Number) ifTrue: [interval := 1 to: anInterval] ifFalse: [interval := anInterval]. ^self basicNew initialize: interval.! ! !NumericSet methods ! add: anElement "Adds anElement to the receiver." self index: anElement ifFound: [:index | self] ifNotFound: [:index | elementCount := elementCount + 1. contents at: index put: 1. index] ifInvalid: [^self error: anElement printString, ' cannot belong to ', self printString]. ^anElement! contents: aBitArray count: anInteger "Private - Set up instance variables for shallowCopy." contents := aBitArray. elementCount := anInteger.! do: aBlock "Evaluate the one-argument block over the elements of the receiver." interval do: [:element | (self includes: element) ifTrue: [aBlock value: element]]! includes: anElement "Answers true if anElement is a member of the receiver." self index: anElement ifFound: [:index | ^true] ifNotFound: [:index | ^false] ifInvalid: [^false].! index: anInteger ifFound: block1 ifNotFound: block2 ifInvalid: block3 "Private - Get index into contents based on anInteger. If bit is true, evaluate block1 with index and return. If bit is false, evaluate block2 with index and return. If index is out of range, evaluate block3 and return." | index | anInteger < interval first ifTrue: [^block3 value]. anInteger > interval last ifTrue: [^block3 value]. index := ((anInteger - interval first) / interval increment) + 1. (index isKindOf: Integer) ifFalse: [^block3 value]. (contents at: index) ~~ 0 ifTrue: [^block1 value: index] ifFalse: [^block2 value: index]! initialize: anInterval "Private - Set up instance variables." interval := anInterval. contents := BitArray new: interval size. elementCount := 0.! maximumSize "Maximum size to which receiver can grow." ^interval size.! remove: anElement ifAbsent: aBlock "Removes the specified element from the receiver. If not found, evaluates aBlock with no argument and answers it." self index: anElement ifFound: [:index | contents at: index put: 0. elementCount := elementCount - 1. ^anElement] ifNotFound: [:index | aBlock value] ifInvalid: [aBlock value]! shallowCopy "Answer a shallow copy of the receiver." ^(self class over: interval) contents: contents count: elementCount! species "Species of NumericSet is Set." ^Set! ! NumericSet subclass: #CharacterSet instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' ! !CharacterSet class methods ! new "Answer a new empty CharacterSet." | temporaries | ^super over: 256! ! !CharacterSet methods ! add: aCharacter "Adds aCharacter to the receiver." ^super add: aCharacter asciiValue! do: aBlock "Evaluate the one-argument block over the elements of the receiver." interval do: [:element | (super includes: element) ifTrue: [aBlock value: (Character value: element)]]! includes: aCharacter "Answers true if aCharacter is an element of the receiver." ^super includes: aCharacter asciiValue! remove: aCharacter "Removes aCharacter from the receiver and answers it. If not present in the receiver, signal an error." super remove: aCharacter asciiValue. ^aCharacter! remove: aCharacter ifAbsent: aBlock "Removes aCharacter from the receiver and answers it. If not present in the receiver, evaluate aBlock and answer the result." super remove: aCharacter asciiValue ifAbsent: [^aBlock value]. ^aCharacter! ! Interval subclass: #CharacterInterval instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' ! !CharacterInterval class methods ! from: beginningCharacter to: endCharacter "Answer an Interval from beginningCharacter to endCharacter incrementing by one." ^self basicNew initBegin: beginningCharacter asciiValue end: endCharacter asciiValue incr: 1! from: beginningCharacter to: endCharacter by: incrementInteger "Answer an Interval from beginningCharacter to endCharacter incrementing by incrementInteger." ^self basicNew initBegin: beginningCharacter asciiValue end: endCharacter asciiValue incr: incrementInteger! ! !CharacterInterval methods ! at: anInteger "Answer the character at index position anInteger in the receiver interval." ^Character value: (super at: anInteger)! first "Answer the first element of the Interval." ^Character value: beginning! last "Answer the last element of the Interval." ^Character value: end! printOn: aStream "Display terse version of receiver on aStream." (Character value: beginning) printOn: aStream. aStream nextPutAll: ' to: '. (Character value: end) printOn: aStream. increment ~= 1 ifTrue: [aStream nextPutAll: ' by: '. increment printOn: aStream]! ! Object subclass: #Link instanceVariableNames: 'value before after ' classVariableNames: '' poolDictionaries: '' ! !Link class methods ! value: anObject "Answer a new unlinked Link with value set to anObject." ^(super new) value: anObject; yourself! ! !Link methods ! addAfter: aLink "Make the receiver the successor of aLink. Answer true if aLink formerly had no successor." self unlink. after := aLink after. before := aLink. aLink after: self. after isNil ifTrue: [^true] ifFalse: [after before: self. ^false]! addBefore: aLink "Make the receiver the predecessor of aLink. Answer true if aLink formerly had no predecessor." self unlink. before := aLink before. after := aLink. aLink before: self. before isNil ifTrue: [^true] ifFalse: [before after: self. ^false]! after "Answer the Link after the receiver, or nil if none." ^after! after: aLink "Private - Set the after instance variable to aLink. One-way and dangerous." after := aLink! before "Answer the Link before the receiver, or nil if none." ^before! before: aLink "Private - Set the before instance variable to aLink. One-way and dangerous." before := aLink! unlink "Remove all links to and from receiver." before notNil ifTrue: [before after: after]. after notNil ifTrue: [after before: before]. after := before := nil! value "Answer the value of the receiver." ^value! value: anObject "Set the value of the receiver to anObject. Answer anObject." ^value := anObject! ! OrderedCollection subclass: #LinkedCollection instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' ! !LinkedCollection class methods ! new "Answer a new, empty LinkedCollection." ^self basicNew! new: anInteger "Answer a new LinkedCollection initially capable of holding anInteger entries. The concept makes no sense for LinkedCollections, so use new." ^self new.! quickNew "Returns a new LinkedCollection that has a Bag inside for fast lookup of entries." ^(self basicNew) setContents: Bag new! ! !LinkedCollection methods ! = aCollection "Answer true if the elements contained by the receiver are equal to the elements contained by the argument aCollection." | link | self == aCollection ifTrue: [^true]. aCollection class == self class ifFalse: [^false]. link := startPosition. aCollection do: [:element | link isNil ifTrue: [^false]. link value = element ifFalse: [^false]. link := link after]. ^link isNil! add: anObject "Answer anObject. Add anObject after the last element of the receiver collection." | link | link := Link value: anObject. (self hookAdd: link) ifTrue: [^anObject]. link addAfter: endPosition. endPosition := link. ^anObject! add: newObject after: oldObject "Answer newObject. Insert newObject immediately after the element oldObject in the receiver collection. If oldObject is not in the collection, report an error." | newLink oldLink | newLink := Link value: newObject. (self hookAdd: newLink) ifTrue: [^newObject]. oldLink := self findLink: oldObject. (newLink addAfter: oldLink) ifTrue: [endPosition := newLink]. ^newObject! add: anObject afterIndex: anInteger "Answer anObject. Insert anObject at index position anInteger + 1 in the receiver collection. If anInteger is out of the collection bounds, report an error." | newLink oldLink | newLink := Link value: anObject. (self hookAdd: newLink) ifTrue: [^anObject]. oldLink := self linkAt: anInteger. (newLink addAfter: oldLink) ifTrue: [endPosition := newLink]. ^anObject! add: newObject before: oldObject "Answer newObject. Insert newObject immediately before the element oldObject in the receiver collection. If oldObject is not in the collection, report an error." | newLink oldLink | newLink := Link value: newObject. (self hookAdd: newLink) ifTrue: [^newObject]. oldLink := self findLink: oldObject. (newLink addBefore: oldLink) ifTrue: [startPosition := newLink]. ^newObject! add: anObject beforeIndex: anInteger "Answer anObject. Insert anObject at index position anInteger - 1 in the receiver collection. If anInteger is out of the collection bounds, report an error." | newLink oldLink | newLink := Link value: anObject. (self hookAdd: newLink) ifTrue: [^anObject]. oldLink := self linkAt: anInteger. (newLink addBefore: oldLink) ifTrue: [startPosition := newLink]. ^anObject! addFirst: anObject "Answer anObject. Add anObject before the first element of the receiver collection." | link | link := Link value: anObject. (self hookAdd: link) ifTrue: [^anObject]. link addBefore: startPosition. startPosition := link. ^anObject! addLast: anObject "Answer anObject. Add anObject after the last element of the receiver collection." | link | link := Link value: anObject. (self hookAdd: link) ifTrue: [^anObject]. link addAfter: endPosition. endPosition := link. ^anObject! after: anObject ifNone: aBlock "Answer the element that immediately follows anObject in the receiver collection. If anObject is not an element of the receiver, aBlock is evaluated (with no arguments)." | link | link := self findLink: anObject ifNone: [^aBlock value]. ^self checkedValue: link after! at: anInteger "Answer the element of the receiver at index position anInteger. If anInteger is an invalid index for the receiver collection, report an error." ^(self linkAt: anInteger) value! at: anInteger put: anObject "Answer anObject. Replace the element of the receiver at index position anInteger with the anObject. If anInteger is an invalid index for the receiver collection, report an error." ^(self linkAt: anInteger) value: anObject! atAllPut: anObject "Answer the receiver after each element has been replaced with anObject." self linksDo: [:link | link value: anObject].! before: anObject ifNone: aBlock "Answer the element that immediately precedes anObject in the receiver collection. If anObject is not an element of the receiver, aBlock is evaluated (with no arguments)." | link | link := self findLink: anObject ifNone: [^aBlock value]. link isNil ifTrue: [^self errorInBounds]. ^self checkedValue: link before! checkedValue: aLink "Private - If aLink is a link, return value. If aLink is nil, generate error." aLink isNil ifTrue: [^self errorAbsentElement] ifFalse: [^aLink value]! consistent "Answer true if receiver link pointers are fully consistent." | otherLink | startPosition before isNil ifFalse: [^false]. endPosition after isNil ifFalse: [^false]. self linksDo: [:link | otherLink := link after. otherLink notNil ifTrue: [otherLink before = link ifFalse: [^false]]]. self reverseLinksDo: [:link | otherLink := link before. otherLink notNil ifTrue: [otherLink after = link ifFalse: [^false]]]. contents notNil ifTrue: [contents size = self size ifFalse: [^false]. self do: [:obj | (contents includes: obj) ifFalse: [^false]]]. ^true! copyFrom: beginning to: end "Answer an OrderedCollection containing the elements of the receiver from index position beginning through index position end." | answer | answer := self emptyCopy. self linksDo: [:link | answer add: link value] from: beginning to: end. ^answer! copyReplaceFrom: start to: stop with: aCollection "Answer a new collection containing a copy of the receiver with the elements at index positions from start through stop replaced with the elements of aCollection." | answer | answer := self emptyCopy: self. self linksDo: [:link | answer add: link value] from: 1 to: start - 1. aCollection do: [:obj | answer add: obj]. self linksDo: [:link | answer add: link value] from: stop + 1 to: self size. ^answer! copyWith: anObject "Answer a copy of the receiver with anObject added to it as an element." ^(self copy) add: anObject; yourself! copyWithout: anObject "Answer a copy of the receiver excluding the first element that equals anObject, if any." ^(self copy) remove: anObject; yourself! do: aBlock "Answer the receiver. For each value in the receiver, evaluate aBlock with that value as the argument." self linksDo: [:link | aBlock value: link value].! emptyCopy "Return a new instance which shares the properties of the receiver, but is empty." contents notNil ifTrue: [^self new contents: contents emptyCopy] ifFalse: [^self new].! emptyCopy: size "Return a new instance which shares the properties of the receiver, but is empty. The size argument is ignored for LinkedCollections." ^self emptyCopy! findFirst: aBlock "Answer the index of the first element of the receiver that causes aBlock to evaluate to true (with that element as the argument). If no such element is found, report an error." | index | index := 0. self linksDo: [:link | index := index + 1. (aBlock value: link value) ifTrue: [^index]]. self errorAbsentElement! findLast: aBlock "Answer the index of the last element of the receiver that causes aBlock to evaluate to true (with that element as the argument). If no such element is found, report an error." | index | index := self size + 1. self reverseLinksDo: [:link | index := index - 1. (aBlock value: link value) ifTrue: [^index]]. self errorAbsentElement! findLink: anObject "Return a Link whose value is anObject. Error if no such Link." ^self findLink: anObject ifNone: [self errorAbsentElement].! findLink: anObject ifNone: aBlock "Return a Link whose value is anObject. If no such link, evaluate aBlock with no arguments." self linksDo: [:link | link value = anObject ifTrue: [^link]]. ^aBlock value.! first "Answer the first element of the receiver. Report an error if the receiver has no elements." startPosition isNil ifTrue: [^self accessEmptyCollection]. ^startPosition value.! firstLink "Answer the first link of receiver." ^startPosition.! grow "Private - Answer the receiver; growing is not necessary for LinkedCollections."! hookAdd: aLink "Private - Answers true if the add has been done within this method; useful for subclasses to get a handle on adds. By default, handles the case of an empty receiver." contents notNil ifTrue: [contents add: aLink value]. startPosition isNil ifTrue: [startPosition := endPosition := aLink. ^true] ifFalse: [^false].! hookRemove: aLink "Private - Allows subclasses to get a handle on removes. By default, handles the case of removing the only element." aLink == startPosition ifTrue: [startPosition := aLink after]. aLink == endPosition ifTrue: [endPosition := aLink before]. contents notNil isTrue: [contents remove: aLink value]! includes: anObject "Answer true if the receiver contains an element equal to anObject, else answer false." contents notNil ifTrue: [^contents includes: anObject]. self do: [:object | object = anObject ifTrue: [^true]]. ^false.! indexOf: anObject ifAbsent: aBlock "Answer the index position of the element equal to anObject in the receiver. If no such element is found, evaluate aBlock (without any arguments)." | index | index := 0. self linksDo: [:link | index := index + 1. link value = anObject ifTrue: [^index]]. ^aBlock value.! isEmpty "Answer true if receiver has no elements" ^startPosition isNil! last "Answer the last element of the receiver. Report an error if the receiver has no elements." startPosition isNil ifTrue: [^self accessEmptyCollection]. ^endPosition value.! lastLink "Answer the last link of receiver." ^endPosition.! linkAt: anInteger "Return a Link whose index is anInteger. If no such link, error." | index | index := 0. self linksDo: [:link | index := index + 1. index = anInteger ifTrue: [^link]]. ^self errorAbsentElement.! linksDo: aBlock "Answer the receiver. For each link in the receiver, evaluate aBlock with that link as the argument." | link | link := startPosition. [link == endPosition] whileFalse: [aBlock value: link. link := link after]. aBlock value: link.! linksDo: aBlock from: start to: stop "Evaluate aBlock on each of the links of the receiver at index positions start through stop." | index | index := 0. self linksDo: [:link | index := index + 1. index > stop ifTrue: [^self]. index < start ifFalse: [aBlock value: link]].! notEmpty "Answer true if receiver has at least one element" ^startPosition notNil! occurrencesOf: anObject "Answer the number of elements contained in the receiver collection that are equal to anObject." contents notNil ifTrue: [^contents occurrencesOf: anObject] ifFalse: [^super occurrencesOf: anObject]! remove: anObject ifAbsent: aBlock "Answer anObject. Remove the element anObject from the receiver collection. If anObject is not an element of the receiver, aBlock is evaluated (with no arguments)." | link | link := self findLink: anObject ifNone: [^aBlock value]. self hookRemove: link. ^link unlink value.! removeFirst "Remove and answer the first element of the receiver. If the collection is empty, report an error." | link | startPosition isNil ifTrue: [self accessEmptyCollection]. link := startPosition. startPosition := link after. self hookRemove: link. ^link unlink value.! removeIndex: anInteger "Answer the receiver. Remove the element of the receiver at index position anInteger. If anInteger is an invalid index for the receiver, report an error." | link | link := self linkAt: anInteger. self hookRemove: link. ^link unlink value.! removeLast "Remove and answer the kast element of the receiver. If the collection is empty, report an error." | link | startPosition isNil ifTrue: [^self accessEmptyCollection]. link := endPosition. endPosition := link before. self hookRemove: link. ^link unlink value.! replaceFrom: start to: stop withObject: anObject "Replace each of the elements of the receiver at index positions start through stop with anObject. Answer anObject." self linksDo: [:link | link value: anObject] from: start to: stop. ^anObject! reverseDo: aBlock "Answer the receiver. For each value in the receiver, evaluate aBlock with that value as the argument. Values are processed in reverse order." self reverseLinksDo: [:link | aBlock value: link value]! reverseLinksDo: aBlock "Answer the receiver. For each link in the receiver, evaluate aBlock with that link as the argument. Links are processed in reverse order." | link | link := endPosition. [link notNil] whileTrue: [aBlock value: link. link := link before.]! setContents: aCollection "Private - Sets the contents instance variable to an auxiliary collection used for fast lookup." contents := aCollection! size "Answer the number of elements contained by the receiver collection." | answer | answer := 0. self linksDo: [:link | answer := answer + 1]. ^answer! ! LinkedCollection subclass: #LinkedSet instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' ! -- cowan@marob.masa.com (aka ...!hombre!marob!cowan) e'osai ko sarji la lojban