Programming Forums

Programming Forums (http://www.programmingforums.org/forumindex.php)
-   Python (http://www.programmingforums.org/forum43.html)
-   -   Mystery (to me) Function (http://www.programmingforums.org/showthread.php?t=11060)

Dietrich Aug 13th, 2006 12:20 PM

Mystery (to me) Function
 
I picked this function up on the net. It is described as an accumulator generator, but I cannot figure out what it can be used for. Any help?
:

def make_acc(start=0):
    """accumulator generator """
    curr = [start]
    def acc(inc):
        curr[0] += inc
        return curr[0]
    return acc


Sane Aug 13th, 2006 12:38 PM

I'm guessing it's meant for debugging, to see how many times a function is being called. The only thing, is there's no argument to handle the function as a parameter. Once I added that in, and a default for "inc", I could make a demonstration.

:

def make_acc(func, start=0):
    """accumulator generator """
    curr = [start]
    def acc(inc=1):
        curr[0] += inc
        return curr[0]
    return acc

@make_acc # use make_acc for debugging purposes
def foo():
    return "bar"

print foo() #1
print foo() #2
print foo() #3
print foo() #4


# don't use "make_acc"
def foo():
    return "bar"

print foo() #bar
print foo() #bar
print foo() #bar
print foo() #bar

So basically, you would append that decorator to any function, and instead of it being called, it will return how times it has been called.

titaniumdecoy Aug 13th, 2006 1:00 PM

I understand how the accumulator function works, but how come the curr variable has to be a list? Why doesn't the following work?

:

def make_acc(start=0):
    """accumulator generator """
    curr = start
    def acc(inc):
        curr += inc
        return curr
    return acc

I get the error "UnboundLocalError: local variable 'curr' referenced before assignment".

Dietrich Aug 14th, 2006 3:33 PM

Wow Sane, I think you hit the hammer on the nail!

I think the list here behaves like a static variable.

titaniumdecoy Aug 14th, 2006 3:51 PM

Quote:

Originally Posted by Dietrich
I think the list here behaves like a static variable.

Yes, but why doesn't curr behave like a static variable when it is not a list?

Dietrich Aug 14th, 2006 4:11 PM

Quote:

Originally Posted by titaniumdecoy
Yes, but why doesn't curr behave like a static variable when it is not a list?

Because in your case the variable would be immutable, simply an integer.
To follow this up with an example:
:

# Python's version of a static variable
def static_num(list1=[0]):
    """The function's default is a list with one element = zero.
    Since the list is a mutable object it retains its address,
    as the function's default is created at compile time.
    Whatever you put into this list as element is retained too"""
    list1[0] += 1
    return list1[0]

print static_num()  # 1
print static_num()  # 2
print static_num()  # 3
...

Well, not quite the same beast! Maybe Sane can help!

Arevos Aug 15th, 2006 6:45 AM

Quote:

Originally Posted by titaniumdecoy
I understand how the accumulator function works, but how come the curr variable has to be a list? Why doesn't the following work?

In a nutshell, it's because Python's syntax does not distinguish between assignment and declaration. Instead, Python has to infer when a variable should be defined from the context; as a rule, it declares a new variable if no variable with the same name exists in the local scope.

Remember that "curr += inc" is equivalent to "curr = curr + inc", so it's an assignment. Because it's the first such assignment in the local scope, it's also a declaration. Thus, curr is first defined, and assigned the value "curr + inc". Unfortunately, because curr is defined before the assignment is evaluated, the curr in "curr + inc" refers to the variable you've just defined. Since you haven't assigned this curr a value yet, an error arises.

This problem can be circumvented by an explicit declaration keyword; let's call it "var". Then, the code would look like:
:

def make_acc(start=0):
    """accumulator generator """
    var curr = start
    def acc(inc):
        curr += inc
        return curr
    return acc

However, because Python does not have this keyword, instead relying on implicit declarations, we instead have to use the next best thing; a mutable variable. The most common mutable variable is a list
:

def make_acc(start=0):
    """accumulator generator """
    curr = [start]
    def acc(inc):
        curr[0] += inc
        return curr[0]
    return acc

This was pointed out to me by a fellow on Slashdot. He argued that Perl was more suited toward a more functional style of programming than Python (or even Ruby), because Perl supports explicit declarations (via my). I have to admit that he's right there. Subfunctions and scoping is one thing Perl does a bit better than its successors.

hydroxide Aug 15th, 2006 2:42 PM

To which I add:
:

def make_acc(start=0):
    def acc(inc):
        acc.curr += inc
        return acc.curr
    acc.curr = start
    return acc

... and now the circle is complete <evil grin>

As to where you'd use an accumulator generator - you could use it with a language where functional programming is more pleasant than OO, and you want to store state for (eg) an accounting program - ie you need to store many totals. You could use an accumulator generator for each. Even with an OO program using one probably decreases the chances of bugs - "=" rather than "+=", for instance. You could probably also use it usefully for something domain-specific-ish in (eg) Ruby since you don't need brackets to call it.

-T. *handwave* "These are not the droids you are looking for" *handwave*

Arevos Aug 15th, 2006 2:53 PM

Aha! A rather elegant solution to the problem you have there, hydroxide.

hydroxide Aug 17th, 2006 8:01 AM

Small bit of background that may help explain part of the comment: I originally posted that code on devshed as what I considered to be a prettier way of writing an acc gen (and by extension any stateful closure). I misread Dietrich's reply as where would _I_ use an acc gen (answer "Never") - sorry, D. Glad someone liked it :D.

Python is probably either getting an "outer" declaration or else the "global" declaration will look in successively outer scopes. Not sure if this will be 2.6 or 3.0 tho.

Cheers, -T.


All times are GMT -5. The time now is 12:50 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC