<<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/
 



Reply via email to