Recursion and Variable Scope
mudpyr8
mudpyr8 at yahoo.com
Fri Sep 7 18:07:38 EDT 2001
More information about the Python-list mailing list
Fri Sep 7 18:07:38 EDT 2001
- Previous message (by thread): Recursion and Variable Scope
- Next message (by thread): ! www.dss-card.net - Unlock your satellite channels and never pay
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Wow! Thank you so much. That was very instructive. I new there was one little piece I was missing that I just couldn't find on my own. I really appreciate the time you took to explain everything. That was an incredible help. - Shane "Alex Martelli" <aleax at aleax.it> wrote in message news:<9n9vln072d at enews1.newsguy.com>... > "mudpyr8" <mudpyr8 at yahoo.com> wrote in message > news:d9f7d05e.0109061911.6bdcda3f at posting.google.com... > > I'm writing a simple function. It is recursive. Here's the code: > > > > ########################## > > def generate(sides, dice, roll): > > if dice > 0: > > for x in range(sides): > > roll.append(x+1) > > generate(sides, dice - 1, roll) > > roll.pop() > > else: > > print roll > > ########################## > > > > When calling it: generate(4, 2, []) > > will generate output of 16 pairs of values, 1-4 each. However, I > > cannot get that result stored in an object; I can only print it. > > > > The last line is the tricky part. Where it says # print roll # I > > want it to do something like: # rolls.append(roll) # . > > Unfortunately, creating a variable # rolls = [] # outside of the > > function definition, and even stating # global rolls # on the > > first line of the block doesn't seem to matter. > > More precisely, if you say: > rolls.append(roll) > you're adding to tolls a reference to the OBJECT to which > roll also refers, *NOT* a copy or snapshot of the way that > object is *currently* made up. Python doesn't do copies > or snapshots behind your back: if you want a copy, you ask > for a copy. So, to clarify, a slight mod to your code: > > rolls = [] > > def generate(sides, dice, roll): > if dice > 0: > for x in range(sides): > roll.append(x+1) > generate(sides, dice - 1, roll) > roll.pop() > else: > print roll > rolls.append(roll) > > (no "global rolls" needed: we are not BINDING rolls, so > the compiler knows it can't be local -- we just call a > method on rolls, and it's irrelevant to this purpose that > the method is a mutator rather than just an accessor:-). > > Now, running this: > > D:\>python -i ro.py > Alternative ReadLine 1.4 -- Copyright 2001, Chris Gonnerman > >>> generate(4,2,[]) > [1, 1] > [1, 2] > [1, 3] > [1, 4] > [2, 1] > [2, 2] > [2, 3] > [2, 4] > [3, 1] > [3, 2] > [3, 3] > [3, 4] > [4, 1] > [4, 2] > [4, 3] > [4, 4] > >>> rolls > [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []] > >>> > > and just for clarity, if we now write in this interactive session: > > >>> rolls[0].append('x') > >>> rolls > [['x'], ['x'], ['x'], ['x'], ['x'], ['x'], ['x'], ['x'], ['x'], ['x'], > ['x'], [' > x'], ['x'], ['x'], ['x'], ['x']] > >>> > > see? rolls has 16 references to the SAME list object -- and, no > surprise about that, since we've always appended to rolls a > reference to the same list object each time. > A list object is mutable, so, as we mutate the list object (to > which 'roll' refers in function generate), all references see > the current (mutated) version. Clear so far...? > > So, simplest is to append a COPY of roll to rolls, e.g. changing > the very last line of my version of generate to: > rolls.append(roll[:]) > now python -i ro.py gives us the same output (same print > statements get executed) but then checking out interactively > what's left in global variable rolls we see: > > >>> rolls > [[1, 1], [1, 2], [1, 3], [1, 4], [2, 1], [2, 2], [2, 3], [2, 4], [3, 1], [3, > 2], > [3, 3], [3, 4], [4, 1], [4, 2], [4, 3], [4, 4]] > > which, I think, is what you desire. > > > > I've tried to wrap my mind around this but am stuck. I need to perform > > further manipulation on the results, but have no object with which to > > do so. I know I could write to a file (maybe, if the file handle works > > within the recursion) and then read it but that seems like a waste of > > time. > > Yes, writing to a file IS a way to ensure a copy of the object > is made at that point (note that *pickling* to a pickler object > would not ensure that: if you pickle a mutable object to the > same pickler repeatedly, it's NOT "snapshotted" each time, you > get when unpickling all references to the value the object had > when FIRST pickled!), but a very round-about one indeed. > > You want a copy, just ask for a copy -- a "full list slice" > roll[:] will do, or import copy and use copy.copy(roll) if > you need better polymorphism (which I don't think you do > here) or prefer the more explicit, readable form (but [:] > is very idiomatic to experienced Pythmen!-). > > I hope this proves instructive regarding the crucial "objects > and references" underpinning of Python. Still, there are of > course alternate approaches to your specific task than having > all those appends and pops from roll and recursion too. My > personal favourite is looking at the lists you're generating > as rather transparent "base-N numbers of X digits each" where > X is dice and N is sides (you have a +1 to each digit, but > that's not crucial of course:-). So just count from 0 to > N**X-1 with digit rollover, and you don't really need the > auxiliary list roll either: > > def gen(sides, dice): > results = [] > current = dice*[1] > while 1: > results.append(current[:]) > n = dice - 1 > while n>=0: > current[n] += 1 > if current[n]<=sides: break > current[n] = 1 > n -= 1 > else: break > return results > > Now: > > >>> gen(4,2) > [[1, 1], [1, 2], [1, 3], [1, 4], [2, 1], [2, 2], [2, 3], [2, 4], [3, 1], [3, > 2], > [3, 3], [3, 4], [4, 1], [4, 2], [4, 3], [4, 4]] > > Personally, I find it clearest to frame such enumeration > problems as "counting in a certain base" (or sequence of > bases, if the dice have different number of sides), but > that's pretty much up to individual taste, of course; > there's no substantial performance difference. (in a > few test runs with 6 6-sided dice gen tends to be 20% > or 30% faster on my box, i.e pretty much in the "don't > worry about it" field:-). I like gen because it's more > flexible -- I can easily optimize by preallocation...: > > def gen1(sides, dice): > results = [0]*(sides**dice) > current = dice*[1] > i = 0 > while 1: > results[i] = current[:] > i += 1 > n = dice - 1 > while n>=0: > current[n] += 1 > if current[n]<=sides: break > current[n] = 1 > n -= 1 > else: break > return results > > for a further 30%-or-so speedup -- and now gen1 is > being almost twice as fast as generate, which may > be already in the useful-speedup zone in some cases > (those in which this generation is the bottleneck > of some important operation of your program). Or, > when I want flexibility instead, I can more easily > wrap gen up as a class with a __getitem__, so it > can be used as the sequence in a for statement (in > Python 2.2 this is not so important, thanks to > generator iterators, but sometimes it may be useful > to keep compatibility with older Python versions). > > Generalizing to differently-sided dice is easier -- > imagine sides is a sequence of dice items, each > a number giving the # sides of that specific die, > then all the changes you need (e.g. to gen1) are: > > def gen2(sides, dice): > import operator > assert len(sides)==dice > results = [0]*reduce(operator.mul,sides) > current = dice*[1] > i = 0 > while 1: > results[i] = current[:] > i += 1 > n = dice - 1 > while n>=0: > current[n] += 1 > if current[n]<=sides[n]: break > current[n] = 1 > n -= 1 > else: break > return results > > basically just testing vs sides[n] rather > than a single number 'sides', and a tiny bit > more work at startup for pre-allocation. > > But I'm sure the recursive approach must have > some advantages too, if it's clearer to you or > more flexible along axes that matter to you. > > > Alex
- Previous message (by thread): Recursion and Variable Scope
- Next message (by thread): ! www.dss-card.net - Unlock your satellite channels and never pay
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
More information about the Python-list mailing list