HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Summation and product functions using functional paradigm

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
andfunctionalproductusingsummationfunctionsparadigm

Problem

Below is the problem:


Show that both summation and product are instances of a more general function, called accumulate, with the following signature:

def accumulate(combiner, start, n, term):
    """Return the result of combining the first n terms in a sequence."""
    "*** YOUR CODE HERE ***"




Accumulate takes as arguments the same arguments term and n as summation and product, together with a combiner function (of two arguments) that specifies how the current term is to be combined with the accumulation of the preceding terms and a start value that specifies what base value to use to start the accumulation. Implement accumulate and show how summation and product can both be defined as simple calls to accumulate:

def summation_using_accumulate(n, term):
    """An implementation of summation using accumulate.

    >>> summation_using_accumulate(4, square)
    30
    """
    "*** YOUR CODE HERE ***"

def product_using_accumulate(n, term):
    """An implementation of product using accumulate.

    >>> product_using_accumulate(4, square)
    576
    """
    "*** YOUR CODE HERE ***"


For which, solution is mentioned below:

```
from operator import add
from operator import mul

def square(x):
""" Retrun x squared"""
return x * x

def accumulate(combiner, start, n, term):
"""Return the result of combining the first n terms in a sequence."""
if n == 0:
return start
else:
start = combiner(term(n), start)
return accumulate(combiner, start, n-1, term)

def summation_using_accumulate(n, term):
"""An implementation of summation using accumulate.

>>> summation_using_accumulate(4, square)
30
"""
assert n >= 0
return accumulate(add, 0, n, term)

def product_using_accumulate(n, term):
"""An implementation of product using accumulate.

>>> product_using_accumulate(4, square)
576
"""
assert n > 0
return accumulate(mul, 1, n, term)

result = summation_using_a

Solution

The start parameter of accumulate is a bit misleading.
My first thought was that start is the first element of the sequence,
but it's an accumulated value.
A more common name is acc or accum.

Reassigning function parameter is considered a bad practice,
in any language as far as I remember.
It's basically reusing an input variable for one additional purpose.
start is an input to your function,
but when you reassign it, it becomes something else:
instead of being just the input,
now it carries the result of a calculation.
When you look at your function body,
the variable start is sometimes the input,
sometimes the result of a local calculation.
This duality in purpose can be confusing and can lead to errors.
It's cleaner to assign the mutated value to a completely new local variable.

In this version, acc always has the same value,
there can be no confusion and misuse, it's perfectly clear:

def accumulate(combiner, acc, n, term):
    """Return the result of combining the first n terms in a sequence."""
    if n == 0:
        return acc
    else:
        next_acc = combiner(term(n), acc)
        return accumulate(combiner, next_acc, n-1, term)


The benefit may not be obvious in this simple function,
but I hope you see the truth in the principle of not reusing parameter variables.
In a longer, more complicated function the benefits would become more clear.

In languages that have out-parameters,
re-assignment of parameters forces me to double-check the signature.
Although Python doesn't have out-parameters,
I still review code like this with suspicion,
in case the author may have mistakenly believed that the re-assigned value will produce a side effect outside the function.
This may sound a bit paranoid, but I've seen this kind of thing happen more than once.
By not re-assigning parameters,
all suspicions are cleared, and the code is easier to review.

For the record, I was tempted to suggest to drop the else clause there,
to simplify to:

def accumulate(combiner, acc, n, term):
    """Return the result of combining the first n terms in a sequence."""
    if n == 0:
        return acc
    next_acc = combiner(term(n), acc)
    return accumulate(combiner, next_acc, n-1, term)


But then, most functional languages return the last evaluation,
and explicit return statements are not recommended,
which makes if-else necessary.
For example the Scala solution would look something like this:

def accumulate(combiner, acc, n, term): Int = {
    if (n == 0) {
        acc
    } else {
        val nextAcc = combiner(term(n), acc)
        accumulate(combiner, nextAcc, n-1, term)
    }
}


So using the explicit else clause is closer to the spirit of functional programming, so I'd leave that as you did.

Code Snippets

def accumulate(combiner, acc, n, term):
    """Return the result of combining the first n terms in a sequence."""
    if n == 0:
        return acc
    else:
        next_acc = combiner(term(n), acc)
        return accumulate(combiner, next_acc, n-1, term)
def accumulate(combiner, acc, n, term):
    """Return the result of combining the first n terms in a sequence."""
    if n == 0:
        return acc
    next_acc = combiner(term(n), acc)
    return accumulate(combiner, next_acc, n-1, term)
def accumulate(combiner, acc, n, term): Int = {
    if (n == 0) {
        acc
    } else {
        val nextAcc = combiner(term(n), acc)
        accumulate(combiner, nextAcc, n-1, term)
    }
}

Context

StackExchange Code Review Q#80392, answer score: 2

Revisions (0)

No revisions yet.