This assignment is a preliminary Smalltalk programming exercise.
Recall from lectures that if your source code is divided into many files (e.g. lib.st classes.st methods.st go.st) then you should create a file "st" with contents...
gst lib.st classes.st methods.st go.st $*
...then make it executable...
chmod +x st
...then you can call it in batch mode using...
./st
... or you can call it in interactive mode (useful for debugging) using
./st -
Recreate a tiny amount of graph.st in Smalltak. Change all the "XXX" items in the following.
Crete a "Range" class that holds "min" and "max".
Object subclass: #Range instanceVariableNames: ' min max' classVariableNames: '' poolDictionaries: '' category: nil !
If new numbers are added to a Range, the "min" and "max" are updated so they hold the minimum and maximum values. For example:
|r| r := Range new. #(10 20 5 30 0 40) do: [:x| r add: x]. r. "should produce ==> 0..50" !
Auto-initialize
! Range class methods ! new "auto-initialize new Ranges" XXX !! ! Range methods ! printOn: aStream "print min..max" XXX ! init "min's first value is very large. max's first value is min * -1" XXX ! add: aNumber "take a number, reset max and min if needed" XXX ! min: aNumber "take a number, reset min if needed" XXX ! max: aNumber "take a number, reset max if needed" XXX ! scale: aNumber "scale a number according to where it falls in the range min..max" XXX "should return a number 0 <= n <= 1" !!
The following code is my current implementation of the "Graph" class. It contains errors and is incomplete:
So do not try to run this code (but guess what the next project will be...).
Write comments on the following code showing that you understand what is going on here.
Object subclass: #Graph instanceVariableNames: 'label matrix height width ox oy xrange yrange ' classVariableNames: '' poolDictionaries: '' category: nil ! ! Graph methods ! frame ox to: (width - 1) do: [:i| self plot: i @ oy with: $- ]. ox to: (width - 1) do: [:i| self plot: i @ (height - 1) with: $- ]. oy to: (height - 1) do: [:i| self plot: ox @ i with: $| ]. oy to: (height - 1) do: [:i| self plot: (width - 1) @ i with: $| ]. ! height: h width: w height := h. width := w. self resize. ! height: h self height: h width: width ! init label := ''. xrange := Range new. yrange := Range new. matrix := OrderedCollection new. self height: 0 width: 0. ox=6. oy=2. ! resize matrix := Array new: height. 1 to: height do: [:i| matrix at: i put: (Array new: width) ]. ! plot: aPoint with: aSymbol (matrix at: aPoint y) at: aPoint x put: aSymbol. ! splot: aPoint with: aString |i| i := 0. aString do: [:elm | self plot: (aPoint x + i) @ aPoint y with: elm. i := i + 1 ] ! width: w self height: height width: w ! xscale: aNumber ^((xrange scale: aNumber) * (width - 1 - ox) + ox) round ! yscale: aNumber ^((yrange scale: aNumber) * (height - 1 - oy) + oy) round !!
Run over the lecture notes (sections 1..7, 10) given in class and the above code. Write down a list of Standard Smalltalk methods used in the lecture notes and the above code (who receives them, what arguments they take, what is the name of the method, what they return). Do not mention any method in a non-standard Smalltalk class (e.g. Graph, Range, Account, CheckedArray, Checking, Complex). No more than 3 lines per method.
That's this project. But if you want to get a jump start on the next project:
"GraphReader" is a factory class that keeps a list of tags, and the classes they trigger.
! GraphReader methods ! init "right now, incomplete. Needs more keywords mapped to classes" ^handler at: 'line' put: #LineTag !!
The read loop for a graph file is then (a) read the next line, (b) find the first word (c) look up who handles that line then (d) ask that class to handle that line.
! GraphReader methods ! process: aFileName "generic read loop" (File name: aFileName) readStream linesDo: [:line| "incomplete: does not handle comment characters" line size > 1 ifTrue: [ self do: line]]. !! do: string |first words thing klassSymbol klass| words := string asWords. thing := words removeFirst. klassSymbol := handler at: thing ifAbsent: [^self cantDo: thing]. klass := Smalltalk at: klassSymbol ifAbsent: [^self cantDo: klassSymbol]. ^klass new do: words for: self !! ! String methods ! asWords "splits a string into a collection of words" ^(self tokenize: '\s+') asOrderedCollection !!
Note the advantage of the above: if you create new tags, you only have to make minimal changes to the main loop of "GraphReader" (just register that class in the "init" method).
The above code assumes that there is a "Tag" hierarchy that responds to "do:for:" that processes a collection of words and writes any side-effects of that processing onto a working memory (the argument to "self").
For example:
! Symbol methods ! isa: class with: vars "my shorthand for creating classes" ^class subclass: self instanceVariableNames: vars classVariableNames: '' poolDictionaries: '' category: nil !! #Tag isa: Object ! #LineTag isa: Tag with: 'expect' ! ! LineTag methods ! do: words for: aGraph "process the words, store the output in aGraph" !!
Warning: as before, there are bugs an incompleteness in the above code.