1
\$\begingroup\$

Q2. Define a function make_accumulator that returns an accumulator function, which takes one numerical argument and returns the sum of all arguments ever passed to accumulator. Use a list and not a nonlocal statement.

Below is the solution:

def make_accumulator():
    """Return an accumulator function that takes a single numeric argument and
    accumulates that argument into total, then returns total.

    >>> acc = make_accumulator()
    >>> acc(15)
    15
    >>> acc(10)
    25
    >>> acc2 = make_accumulator()
    >>> acc2(7)
    7
    >>> acc3 = acc2
    >>> acc3(6)
    13
    >>> acc2(5)
    18
    >>> acc(4)
    29
    """
    lst = []
    def accumulate(n):
        lst.append(n)
        length = len(lst)
        total = 0
        for index in range(length):
            total = total + lst[index]
        return total
    return accumulate

Can we improve this solution?

\$\endgroup\$
1
  • \$\begingroup\$ """How can I think of writing pure functional code for above program(i.e., without any state change)? As per above code, I think closure concept does not allow pure functional programming. """ \$\endgroup\$ Commented May 16, 2015 at 8:13

2 Answers 2

2
\$\begingroup\$

An alternative to the closure is to add an attribute to the function, so rather than:

lst = []
def accumulate(n):
    lst.append(n)
    ...

you have:

def accumulate(n):
    accumulate.lst.append(n)
    ...
accumulate.lst = []

It's a little more typing, but one advantage is that it allows easier access to the list so far from outside the function:

>>> accum = make_accumulator()
>>> accum(7)
7
>>> accum(8)
15
>>> accum.lst
[7, 8]

With the closure, this would be accum.func_closure[0].cell_contents.


Using indices is not a very Pythonic way to handle iterating over a list, and x = x + y can be simplified to x += y; compare:

...
length = len(lst)
for index in range(length):
    total = total + lst[index]
...

with:

...
for item in lst:
    total += item
...

There is also sum(lst), which the specification doesn't forbid.


How can I think of writing pure functional code for above program (i.e., without any state change)?

I don't think that this can be achieved in a purely functional way as specified - you are explicitly told to use a list to store the values passed, so there will always be state, and the function you create will give different outputs for the same inputs depending on the previous inputs (no referential transparency).

\$\endgroup\$
3
  • \$\begingroup\$ I like your answer!!! When do I think of "adding attribute" vs "using closure"? adding an attribute to a function object is something new to me ): But looks very amazing by seeing such code. \$\endgroup\$ Commented May 16, 2015 at 11:48
  • \$\begingroup\$ Is it for item in accumulate.lst or for item in lst? \$\endgroup\$ Commented May 16, 2015 at 12:00
  • 1
    \$\begingroup\$ I generally use a function attribute when I might want easy access from outside the function, closures when I probably won't. I think the former is more explicit, too. Whether it's lst or accumulate.lst depends on whether you use closures or attributes. \$\endgroup\$
    – jonrsharpe
    Commented May 16, 2015 at 12:04
2
\$\begingroup\$

I've been a bit puzzled by the requirement to use a list: if we only need the total sum in the end, and there's no requirement to access previously passed parameter values, then why waste memory? That's outright silly.

Unless, the assignment means to "use a list" in this way:

def make_accumulator():
    lst = [0]
    def acc(n):
        lst[0] += n
        return lst[0]
    return acc

Instead of a list, you could use any other kind of object. It just has to be an object, cannot be a simple value. For example, it won't work with a simple integer variable, like total += total, because that involves a reassignment (rebinding of a name), which is very different from modification of an existing object. For more details, see PEP227.

How can I think of writing pure functional code for above program (i.e., without any state change)?

You cannot. As per the requirements, the accumulator function may (and in practice, it typically will) return different values for the same input, which necessitates state. There's no way around it.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.