Object Collection SequenceableCollection ArrayedCollection
from the class comment:
My instances provide space-efficient storage of data which tends to be constant over long runs of the possible indices. Essentially repeated values are stored singly and then associated with a "run" length that denotes the number of consecutive occurrences of the value. My two important variables are runs An array of how many elements are in each run values An array of what the value is over those elements The variables lastIndex, lastRun and lastOffset cache the last access so that streaming through RunArrays is not an N-squared process.
The values of the cache variables are set each time an element is accessed. The value ranges of these variables are:
lastIndex between: 1 and: self size lastRun between: 1 and: runs size lastOffset between: 0 and: (runs at: lastRun) - 1
The following expression holds after execution of acess method at:
| lengthOfPreviousRuns | lengthOfPreviousRuns := (1 to: lastRun - 1) inject: 0 into: [:sum :runIdx | sum + (runs at: runIdx)]. lastIndex = (lastOffset + lengthOfPreviousRuns + 1)
This is the postcondition of at:
The instance method
at: idx setRunOffsetAndValue: aBlock
is used to map an index idx into a run. The method parameter aBlock requires
three block arguments:
[:run :offset :value | <instructions>]
where
All enumeration methods of RunArray that use the method do: evaluate the block once for each element in the RunArray, not once for each run! This is an important difference. Look at this example:
| runArray cnt result | runArray := RunArray new. runArray addLast: #hello times: 5000. cnt := 0. result := runArray allSatisfy: [:element | cnt := cnt + 1. element = #hello ]. (Array with: result with: cnt) inspectThere is a method that can be used to execute a block once per run. Look at this code:
| runArray cnt result | runArray := RunArray new. runArray addLast: #hello times: 5000. cnt := 0. result := true. runArray runsAndValuesDo: [:runLength :value | cnt := cnt + 1. result := result & (value = #hello) ]. (Array with: result with: cnt) inspectIt is tempting to redefine some enumeration methods in RunArray. As an example, here is a local redefinition for allSatisfy:
allSatisfy: aBlock "Evaluate aBlock with the elements of the receiver. If aBlock returns false for any element return false. Otherwise return true." values do: [:each | (aBlock value: each) ifFalse: [^ false]]. ^ trueNote that for blocks with side effects (like the ones in the examples above) this method will exibit a behavior that differs from the behavior of the inherited method. Note also that is is not possible tosimple redefine the method do: in RunArray since this would change the behavior of methods collect:, select: and reject:.
Class Text
Inherits From:
Object Collection SequenceableCollection ArrayedCollectionfrom the class comment:
I represent a character string that has been marked with abstract changes in character appearance. Actual display is performed in the presence of a TextStyle which indicates, for each abstract code, an actual font to be used. A Text associates a set of TextAttributes with each character in its character string. These attributes may be font numbers, emphases such as bold or italic, or hyperlink actions. Font numbers are interpreted relative to whatever textStyle appears, along with the text, in a Paragraph. Since most characters have the same attributes as their neighbors, the attributes are stored in a RunArray for efficiency. Each of my instances has string a String runs a RunArray
Instances of class Text are used to store stylished strings.
The following condition is required to be always true:
string size = runs sizeIn pettern terminology, Text is a wrapper. It wraps a string and keeps additional information that connot be kept in the string itself. Method Text class>>fromString: does not create a copy of the string precisely because Text is a wrapper.
Two instances of Text should not share the same string or the same run array.