Table of Contents
This document implements fizzbuzz in smalltalk. It was created as part of an application for the Recurse Center.
Write a program that prints out the numbers 1 to 100 (inclusive). If the number is divisible by 3, print Crackle instead of the number. If it's divisible by 5, print Pop. If it's divisible by both 3 and 5, print CracklePop. You can use any language.
Extending class Integer
The GNU-Smalltalk Tutorial has an example where Number
is extended to calculate the radius and circumference of circles. It formed the basis for my approach here. In part because it expresses the way Smalltalk can be used and takes me out of my comfort zone.
Number extend [ radiusToArea [ ^self squared * Float pi ] radiusToCircumference [ ^self * 2 * Float pi ] ]
C-like fall through
I started out trying to find a switch/case/cond
control structure. When Googling produced some exotic approaches via StackOverflow, I reckoned I might be swimming against a tide.
Early attempts at this implementation ran into the difficulty of not printing the default case when something else matches. The solution to use ^nil
suggests how ugly this approach is.
Integer extend [ fb [ (self \\ 15 = 0) ifTrue: ['fizzbuzz' printNl. ^nil]. (self \\ 3 = 0) ifTrue: ['buzz' printNl. ^nil]. (self \\ 5 = 0) ifTrue: ['fizz' printNl. ^nil]. self printNl. ^nil. ] ]
As a Decision Tree
Since Smalltalk does not have an equivalent to switch
or case
or Lisp's cond
. This probably reflects the fact that each message to which an object responds must be explicitly defined and switch/case/cond
is designed to accept an arbitrary number of arguments. While it might be possible to generate a set of messages that cover many likely arities Object
would receive, it probably is not worth building into the base language.
This is the source for fizzbuzz.st
:
Integer extend [ fizzbuzz [ (self \\ 15 = 0) ifTrue: ['fizzbuzz' printNl] ifFalse: [ (self \\ 3 = 0) ifTrue: ['fizz' printNl] ifFalse: [ (self \\ 5 = 0) ifTrue: ['buzz' printNl] ifFalse: [self printNl] ] ] ] ] <<loop-over-interval>>
Smalltalk Notes
Printing Strings
Most Smalltalk tutorials begin with something like:
'Hello World' printNl " -> 'Hello World' "
The subtilty this glosses over is that most of the time when using strings, people don't want the string's Object
represention with the quotation marks. They want Hello World
not 'Hello World'
. Smalltalk being Smalltalk, it is certainly possible to redefine the message printNl
for String
. But there is a built in alternative displayNl that strips off the quotation marks.
Working with directories in the REPL
The gst
REPL can be used for regular systems programming. The Directory
object will provide the current working directory using the message Directory working
. It will allow changing the working directory using the message Directory working: 'some-directory'
.
Crackle Pop
Extend Integer
The source for cracklePop.st
:
"cracklePop.st Print out the numbers 1 to 100 (inclusive). + If the number is divisible by 3, print Crackle instead of the number. + If it's divisible by 5, print Pop. + If it's divisible by both 3 and 5, print CracklePop. To load the program use: FileStream fileIn: 'cracklePop.st' " Integer extend [ cracklePop [ (self \\ 15 = 0) ifTrue: ['CracklePop' displayNl] ifFalse: [ (self \\ 3 = 0) ifTrue: ['Crackle' displayNl] ifFalse: [ (self \\ 5 = 0) ifTrue:['Pop' displayNl] ifFalse: [self displayNl] ] ] ] ] <<loop-over-interval>>
Loop over the interval
By looping standards, iterating over an index in Smalltalk at first feels a bit indirect uses the Interval
object. But after thinking about it a bit, I realized that this is sort of prelude to some languages' generator syntax (even if it is not lazy).
It also feels a bit verbose, but after realizing it replaces symbols with letters composing words, it probably is about the same number of characters and closer to ordinary English. Not trying to conform to the structure of Algol and Fortran and C is one of the things I have really started to appreciate about Smalltalk.
1 to: 100 by: 1 do: [:i | i cracklePop]