patternpythonMinor
Summation and product functions using functional paradigm
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:
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:
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
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
My first thought was that
but it's an accumulated value.
A more common name is
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.
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
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,
there can be no confusion and misuse, it's perfectly clear:
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
to simplify to:
But then, most functional languages return the last evaluation,
and explicit
which makes
For example the Scala solution would look something like this:
So using the explicit
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.