Last published: August 6, 2007 by 'xp'
Defines 5 Classes
Extends 13 Classes
NumericCollections adds the four basic numeric methods (+, -, *, /) to collections as well as some other methods that come as a natural follow on.
For years now, I have seen too many instances of ST programmers writing loops which mulitply the elements of one collection by the other. Or decrementing all the values by a single value. Or summing. Etc.
Basic Numeric Protocol
Collections can be sent any of the four basic math messages. And you can make collections be the arguments too. Some examples:
#(1 2 3 4) + #(5 6 7 8) --> #(6 8 10 12)
(Bag with: 2 with: 2 with: 6) / -2 --> Bag (-3 -1 -1)
0.0d * (Set with: 4 with: 5 with: 6) -> Set (0.0d)
#(10 20 30 40) * (4 @ -2) --> (Array with: 40@-20 with: 80@-40 120@-60 with: 160@-80)
Furthermore, when the reciever is an SequenceableInstance, it is possible to use the C style math/assignment operators, so that the receiver is modified, rather than a new instance created.
#(1 2 3 4) /= 5 --> #((1/5) (2/5) (3/5) (4/5))
More Numeric Protocols
Once you've done those four, it is possible and logical to write some others. Some of the more popular ones are sum, min, and max. The min and max will work on anything that understands min: or max: (such as Times and Dates). Browse the method protocols 'numeric operations', 'statistic operations', 'shewart controls', and 'fitting' in Collection and SequenceableCollection. An example is plotting a collection of x and y values in a given box on the screen. Sooner or later, you have to compute the actual pixel points from the x and y values. Using the collection stuff, it might look something like this:
(xVals - xVals min / xVals range) @ (1 - (yVals - yVals min / yVals range)) * box extent + box origin
which is much more concise than the loops and vars one might expect otherwise.
One of the things that we found ourselves doing a lot of is wanting to enumerate over every nth element of a sequence. This is particularly applicable in image processing where colors are usually stored as repeating RGB values. For example, we may want to compute the standard deviation or average of the red content of an image. ReindexedCollections, are collection wrappers which change the indexing scheme. They actually abstract and genericize the concept of reverseDo:. We don't send that message any more, we just send something like:
(sequence by: -1) do: [:each | ...work, work, work...]
The median for the red channel of an 32bit image would be:
(Image fromUser bitsInstVar from: 4 by: 4) median
This whole thing gets sticky with strings. Because strings are sequenceable collections, and if we do nothing, then it ends up wanting to do math with characters. So we could teach characters how to be numbers or... we could interpret strings as numbers. It is more likely that you are reading numeric token strings and wanting to do ops on the implied number by them, than multiply character digit values. So, it is also possible to do:
'20' + 5 --> 25
Strings are always treated as the lowest number form and are coerced up. If two strings are op'ed, then you will get a string representation back:
'20' / '10' --> '2'
They even nest inside normal collections fine:
#(3 '2' '8') * #('4' '5' 6) --> #(12 '10' 48)