<<Alistair: would it be OK with you if I write an article refactoring
this solution?>>
<<it is a very interesting implementation:
what it does is rather compact and contains some good ideas.
According to my limited understanding of good, much less
elegant Smalltalk code, however, it could use some work.
>>
Looking forward to it, Ron. I understood and understand those things
you mentioned, but as I tried the multi-function version, decided
that 10 lines of loop code with decent naming was
small enough to fit in a person's eye and therefore worth leaving in
a single place where it could all be stared at in one go; compared
with clicking over multiple methods and having the cognitive load of
switching mental contexts (plus passing a bunch of state around).
So I look forward to seeing what you can come up with.
<<I offer these thoughts in the spirit of learning
and self improvement, with the greatest respect
for my fellow programmers, and because it's my birthday.>
Yayyyy! Are you yet the oldest living, still-programming Smalltalker?
<<I like the "frameStart" indexing in your code. I don't like
the "rollIndex" indexing in my code, but when I "fix" one problem,
it just creates another:>>
Ah, well, as Ron would testify, I've been editing and tweaking this
code for several days (I've been sending him the sad intermediate
versions). I kept being bothered by this rollIndex thing, and finally
dawned on me that the idiot thing was only used as the indicator
of where the start of the frame is. So I renamed it and felt much
better.
>> open := roll1 + roll2 < 10.
>> spare := (roll1 < 10) and: (roll1 + roll2 = 10).
>> strike := roll1 = 10.
<<Have you considered reversing the order and using not? I think
that reversing the order and using 'not' would make it (at least
more)
clear that only one could be set, and always one will be set.)>>
I have no trouble reversing the order to:
strike := roll1 = 10.
spare := (roll1 < 10) and: (roll1 + roll2 = 10).
open := roll1 + roll2 < 10.
but it should be clear somehow that they are mutually exclusive ---
two of them can't possibly be set.
>> ( open or: spare ) ifTrue: [ frameStart := frameStart + 2 ].
>> ( strike ) ifTrue: [ frameStart := frameStart + 1 ].
<<I guess the block in the first line could be an "else" to the
second line.>>
I tried that and finally decided that I like the explicitness of this
reading. Earlier versions didn't have the explicit open/spare/strike
flags either, but I got tired of trying to remember what all the
combinations meant and finally decided to try a version in which
everything was laid out explicitly for the next reader. I ever tried
doing
( open ) ifTrue: [ roll3 := ) ] ifFalse: [ roll3 := ...+2].
score := score + roll1 + roll2 + roll3
but decided that the unwary reader would be sidetracked by
seeing roll3 for open frames.
<<Very elegant, Alistair!>>
Thank you, Victor. (Even taking Ron's notes into account)
<< <<I felt pangs for the lack of ++ and 'break'>> The break issue
can be solved in two different ways:
1. Using the whileTrue <whileFalse> family of methods
2. Create your own method for the loop, with a return when a given
condition happens.
>>
I'm not sure the whileTrue: block satisfies the need.
Here's what I /was/ aiming for. The lack of 'break' forced me down
a different path:
10 timesRepeat: [
( strike ) ifTrue: [ stuff... . break.].
( spare ) ifTrue: [ stuff... . break.].
( open ) ifTrue: [ stuff... . break.].
]
I did try, for a few seconds, making a function for each of those,
e.g., handleStrike:, etc., but was troubled by the amount of state
I'd have to pass in. So I decided to keep everything in one routine.
(we'll see what Ron can come up with in this regard).
The nice thing about the final version is that creating actual Frames
is minor given the code that exists. Every frame is set up as a trio
of rolls, and the code structure supports setting the roll-trio
values.
The trio is added to a Collection to make a game of frames. Only
about three lines are different.
The underlying abstraction in this whole approach is that every
frame has three rolls. It's only to save paper (imaginatively
reconstructing history) that the bowling sheets don't have the third
one printed for frames 1-9. If they did have them printed and
people manually copied the bonus values in, this exercise would
be a lot easier.
<< << after I finally gave up with objects (massive) and TDD (clunky)
>> >>
<<You didn't give up objects, you just threw out superfluous
objects. ...
The Smalltalk workspace allows to do a lot of experimentation before
or in
parallel with TDD. VisualWorks ST has a great refactoring browser,
where it
is a pleasure doing TDD. >>
Ah, sorry, my sentence incorrectly maligned both objects and TDD
instead of my own thinking. The fault was not with them but with
my initial designs. I suppose if I wrote a journal as Ron did, you'd
see stuff like this ...
"So here I am trying to make a /language/ with which to discuss
the solution, so of course I'll want to refer to a Frame, and an
OpenFrame, and a Spare, and a Strike ... [ insert creation of four
new classes ] ... etc. a while, ... and egad! why am I sitting here
with 6 classes just to implement a scoring algorithm? and what
problem am I trying to solve anyway, and where do I think I'm
headed with all this junk ? ... [ scrap solution and start over ]
etc. ..."
So it was the "thinking in objects" solution that was massive, not
objects per se. I forgot both the problem assignment and YAGNI
while going down that track.
"... Now forcing myself to stay with only 1 class and growing the
solution TDD, let's use an intentional programming style. The
idea is to expand the roll array (which is conceptually a
compressed thing), so staying with the intentional style, we'd
write
score
expandedRolls := self expandRolls.
score := expandedRolls summed.
^ score
and now the frame complexity moves into the expansion function,
and scoring becomes trivial
(/ aside: whine / why doesn't Smalltalk have a decent set of
intentional names for inject:into:? I wrote 'summed' above because
I can't stand writing
score := expandedRolls inject: 0
into: [ :sum :roll | sum + roll ].
I mean, how is anyone to guess what my intention is with that?
summed arrays and other inject:into: idioms must happen all over
the place... /end aside: whine /)
"
However, after I'd sent that version off to Ron and we both agreed
it wasn't very pretty, I decided to simply write the algorithm from
scratch. The TDD part paid off in two ways,
(1) that developing the 'spare' separately from any other test is
when I got the idea that the roll set is a compressed thing and
every frame has conceptually 3 rolls; (2) afterwards, when I was
changing the code like a maniac, I just happened to have
a set of tests lying around to protect the result.
So it wasn't TDD that was clunky, it was the code that grew out
of my TDD approach. I needed to stare at the whole thing
'en masse' to see any tidying principle. No doubt there's a lesson
there somewhere for the inquiring mind.
Alistair
To Post a message, send it to: [EMAIL PROTECTED]
To Unsubscribe, send a blank message to: [EMAIL PROTECTED]
ad-free courtesy of objectmentor.com
Yahoo! Groups Links
<*> To visit your group on the web, go to:
http://groups.yahoo.com/group/extremeprogramming/
<*> To unsubscribe from this group, send an email to:
[EMAIL PROTECTED]
<*> Your use of Yahoo! Groups is subject to:
http://docs.yahoo.com/info/terms/