[Tutor] Fixed freezing problem, have new one (fwd)

ahimsa ahimsa@onetel.net.uk
Tue Jan 14 16:51:02 2003


On Mon, 2003-01-13 at 01:01, Danny Yoo wrote:

> Ok, I'm back!
> 
Hope your lunch was good Danny :)

> 
> Don Arnold answered your first question, so I can skip to the next
one:

He did - and the learning never stops!!! Even about dice!

> > The module (or function) then initialises a control structure - the
> > 'while' loop - that sets up the minimum limit as an error control -
i.e.
> > if the dice is not rolled, there can't be any output.
> 
> Right: if we say something like "Roll zero numbers of dice", we should
get
> some kind of empty output from the program.  But we want to be careful
> what we mean by "empty" though: do we want to literally get the word
> "empty"?

Hmmmm - maybe I've lost the plot, but if the 'while' lopp controls the
program by preventing it from beginning if no dice are rolled, then
'rolling zero numbers of dice' really would be 'nothing happens' - as in
the program cannot begin because the first condition is not met. So I'm
not sure how anything would happen after that point regardless of the
hypothetical output of zero dice rolling (what is the sound of one hand
clapping anyway?). What am I missing?


> ###
> def dice(number, sides):
>     if number <= 0:
>         return "empty"
>     ...
> ###

Why would this need to be included as part of the (function - is this
what this is called or is it a module?) when it could be established as
the first line of the program that decides whether or not this
function/module should even be called. For me, and using a totally
off-track analogy, it is a bit like this: while doorbell == ringing,
open door. If doorbell != ringing, ergo door does not get opened. So why
could the 'null' of this be controlled using the initial while, rather
than including it in the definition as you seem to have done above?


> or should we use the None value, or something else?  It may seem a
little
> nitpicky, but it actually does matter if we go for a recursive
definition
> of dice().  We can talk about this if you'd like.
> 
> Yes, 'random' is a module that's in the Standard Library.  It's
> specialized for generating "pseudorandom" numbers.  There's some
> documentation about the 'random' module here:
> 
>     http://www.python.org/doc/lib/module-random.html
> If you read this, I'd recommend skipping down near the middle where
the
> docs describe the "Functions for integers"; the stuff at the top and
> bottom is also very useful, but a bit specialized.
> 

Thanks, I'll chase that link down. There is a lot of info about the
modules to read up on, so that's as good a place as any to start.

> Tthat's because the code needs a little retooling: it's doing more
work
> than it really needs to do.  I didn't want to just roll in refinements
to
> the code all at once without some kind of feedback.  Guess it's time
to
> charge forward!

OK ... charging forward ...

> 
> Let's take a look at it again:
> 
> ###
> def dice(number,sides):
>     while number > -1 :
>         value = random.randint(0, sides)
>         number = number + 1
>         return value, dice(number - 2, sides)
> ###
> 
> As an intermediate step, I'm going to do something that might look a
> little weird and arbitrary:  I will change that 'while' loop into an
'if'
> block.
> 
> ###
> def dice(number,sides):
>     if number > -1 :
>         value = random.randint(0, sides)
>         number = number + 1
>         return value, dice(number - 2, sides)
> ###
> 
> This has the same effect as the original code because, even though the
> original code used a "while loop", there was no looping involved due
to
> that 'return' statement at the end of the loop body.  But let's double
> check that this still works.
> 
> ###
> >>> dice(3, 6)
> (6, (0, (3, (0, None))))
> ###
> 
> That's a bit odd.  It's giving us FOUR dice rolls instead of three! 
The
> original code had the same problem because the condition we're
checking,
> 
>     number > -1
> 
> is too permisive: we should really be checking if we're rolling a
nonempty
> number of dice:
> 
>     number > 0.
> 
> 
> Let's make that change:
> 
> ###
> def dice(number, sides):
>     if number > 0:
>         value = random.randint(0, sides)
>         number = number + 1
>         return value, dice(number - 2, sides)
> ###
> 
> Let's try that again.
> 
> ###
> >>> dice(3, 6)
> (6, (0, (0, None)))
> >>> dice(3, 6)
> (4, (4, (1, None)))
> >>> dice(3, 6)
> (5, (0, (3, None)))
> >>> dice(3, 6)
> (4, (5, (5, None)))
> >>> dice(3, 6)
> (6, (2, (6, None)))
> >>> dice(3, 6)
> (1, (1, (2, None)))
> ###
> 
> 
> Ah, better.  Sorry about getting sidetracked there.  *grin*
> 
> 
> Let's go back to your question.
> 
> > Why then would you increment the number through 'number+1'? And,
again,
> > why have the value returned as 'dice(number-2, ...' - why would you
> > subtract the 2?
> 
> That's because it's doing too much work.  If we go one step forward,
and
> two steps backwards, we end up one step backward.  But how do we see
this
> without just stating it?  And how do we fix it?
> 

OK - I need to butt in here. I followed your explanation that the while
loop was not really doing what a while loop is supposed to do because of
the return statement; I also followed substituting the 'if' block, and
also that the -1 was too 'permissive' (I assume because 0 is greater
than -1 which would screw up the idea that if the dice aren't rolled/no
dice are rolled  x times, still yields a value of 0 - is that what you
were meaning - and so correcting that by stating explicitly that it had
to be a greater number of rolls than zero?). I'm stumbling over the
'number + 1'. Why are you doing that? What does the number + 1 do that
is needed by the program that number by itself wouldn't do? And this
gets even more confusing for me, because then later you are taking 2 off
( - 2 ). Taking 1 step fwd and 2 back *does* leave one 1 step back, but
what does this have to do with programming a dice rolling program? There
is obviously something that I am missing here about how you are
conceptualising the components of a dice-rolling program. I would have
approached it as (sorry, not fluent enough in code or psuedocode to
write it out) something like: number of dice x number of rolls x 6
(still thinking of only 6-sided dice here) = whatever. The random module
would be entered ... well, this is where my conception of the problem
breaks down. I know that the random aspect needs to be included to
represent the randomness of an actual (physical reality) dice throw, but
not sure where to put it in. I'm not sure how else to ask this one,
because this is the major stumbling point for me I reckon: how you are
conceiving of the problem, and then conceiving of the solution. 

As an aside, I have just started (literally) reading the book by Abelson
and Sussman "Structure & Interpretation of Computer Programs"
http://mitpress.mit.edu/sicp/full-text/book/book.html and something they
write in their 'Preface to the First Edition' really resonates for me
and since it is related to my point above, I'll share it here:
"[computer language] is a novel formal medium for expressing ideas about
methodology" and later in the same section "The computer revolution is a
revolution in the way we think and in the way we express what we think.
The essence of this [...] is the emergence of what might best be called
*procedural epistemology* - the study of the structure of knowledge from
an imperative point of view [...]". And I reckon that does sum up where
I am coming from with this question group Danny (and others): learning
how to think about problems in such ways that the solutions proposed to
those problems can be formally expressed in computational form. I
suspect that once I am able to acquire that skill, then the formalities
of the language itself become secondary. Anyway, I digress slightly, but
I'd be interested in your perspectives on this too.

> Let's do something unusual by trying to follow an artificial
programming
> restriction: we won't allow "rebinding" a variable name that's already
> defined, but instead we'll add a new temporary variable.  In technical
> terms, we'll be "Introducing a Temporary Variable".  That is, we're
not
> going to do any variable name reassignments.

Are you saying that, for e.g.: you will not assign 1 to x and then
assign 3 to x later, but rather, will make y take on the value of 3
instead of x?

> It's a little weird, but we'll see in a moment why this is a good
idea.
> What does dice() look like if we follow this restriction?
> 
> ###
> def dice(number, sides):
>     if number > 0:
>         value = random.randint(0,sides)
>         next_number = number + 1
>         return value, dice(next_number - 2, sides)
> ###
> 
> 
> That programming rule --- no rebinding --- that we followed above
isn't
> actually as arbitrary as it seems.  It suddently allows us to do
> "plugging-in" safely.  Whereever we see 'next_number', we can now
> "plug-in" and substitute the right-hand side value of 'next-number'
and
> still have a program that behaves the same way:
> 
Sorry - I'm not getting this very clearly. I think I am, then try to
explain it to myself and realise that I haven't got it. (a) Why are you
doing this? What are you attempting to demonstrate? and (b) what are you
meaning by 'plugging in'? It is probably really obvious but I still
can't see it.
Was this to demonstrate that we are taking 1 step forward and 2
backwards in computational terms (i.e. that the program is working too
hard)?

> ###
> def dice(number, sides):
>     if number > 0:
>         value = random.randint(0, sides)
>         next_number = number + 1
>         return value, dice((number + 1) - 2, sides)
> ###
> 
> 
> Once we've done this, we can just drop the initial assignment to
> 'next_number', since 'next_number' is a useless variable that's not
being
> used anywhere anymore.
> 
> 
> ###
> def dice(number, sides):
>     if number > 0:
>         value = random.randint(0,sides)
>         return value, dice(number + 1 - 2, sides)
> ###
> 
> I don't think I need to outline the next step.  *grin* But let's see
what
> dice() looks like now:
> 
> ###
> def dice(number, sides):
>     if number > 0:
>         value = random.randint(0, sides)
>         return value, dice(number - 1, sides)
> ###

> Does this revision make more sense?
> 
> 
> 
> Please feel free to ask more questions about this; I know I went fast
on
> that one, but it's an exciting topic: if we're careful about
reassignment,
> we can do controlled manipulation of our programs to clean them up. 
It's
> almost like factoring math equations to make them easier to
understand.

I can tell that there is a lot for me to learn here, because I was
obviously pretty lost at times. I must confess though that it does seem
a little less daunting than it did initially and makes a pseudo-sense to
me as long as I don't probe that sense too critically. I think I am
getting the drift of what you were attempting but would definitely not
even know how to approach doing it myself. I am keeping this and as I
continue with my readings and practice will refer back to this as a
touchstone of sorts. 
Thank you for taking so much effort in doing this Danny, and I do
apologise for the basic level of my questions. If this level is too
tedious for this list I'm happy to take this off-list if that would be
preferable.

I will soldier on.
Very much appreciated.

Andrew

-- 
ahimsa <ahimsa@onetel.net.uk>