A few weeks ago I discovered the Code Kata website. Working through a few of these exercises may help me improve my very basic squeak which I have wanted to learn properly for a while. As a recovering perler, the first kata that really appeals is kata 4 (data munging).
I started with the following:
- Damien Cassou’s fantastic Squeak development image with code completion, syntax highlighting and much more. That is available here.
- A pure smalltalk regex package. If you download this mcz and drag it and drop it onto your image you are good to go.
- The Terse guide to Squeak
After working through the problems the first time in the workspace, I wanted to come up with a more generic solution. The obvious one is to have a stream method that iterates over lines. There are a number of ways of doing this. With some inspiration from the STL algorithm library here is a class with a single class method:
StreamAlgorithms>>class on: aStream eachLine: aBlock | line | aStream position: 0. [ (line := aStream nextLine) isNil ] whileFalse: [ aBlock value: line ]
This is used as following (actual solution to the Kata elided):
| fp | Transcript clear. fp := CrLfFileStream readOnlyFileNamed: 'c:/dev/weather.dat'. StreamAlgorithms on: fp eachLine: [ :line | Transcript show: ('[', line, ']') ; cr ]. fp close.
Alternatively, here is a class that wraps a stream.
Object subclass: #ExtendedStream instanceVariableNames: 'stream' classVariableNames: '' poolDictionaries: '' category: 'Katas'
These are the instance methods
stream ^ stream stream: anObject stream := anObject eachLine: aBlock | line | self stream position: 0. [ (line := self stream nextLine) isNil ] whileFalse: [ aBlock value: line ]
And the class methods…
ExtendedStream>>class "'Constructor'" on: aStream ^ (self new) stream: aStream "I liked this class method from StreamAlgorithms" on: aStream eachLine: aBlock | line | aStream position: 0. [ (line := aStream nextLine) isNil ] whileFalse: [ aBlock value: line ]
The client code for this is:
Transcript clear. fp := ExtendedStream on: (CrLfFileStream readOnlyFileNamed: 'c:/dev/weather.dat'). fp eachLine: [ :line | Transcript show: ('[', line, ']') ; cr ]. fp close.
Possibly the best way is to put eachLine: as an instance method on the stream class but I’m not comfortable messing with the core libraries just yet.