Subsequent Application of Python First Class Functions: Any Way Cleaner Than Nesting? - python

I'm working an example to help me learn how to use first-class functions in Python. In general, I'm satisfied with the solution I came up with, except for one line of code that screams "un-Pythonic" to me.
So the problem I'm working with is defined here. The puzzle seeks the single permutation (out of 720 possible) of six simple functions involving "2" that ultimately returns -3.
Here's my solution, which simply dumps every possible six-function permutation and its result.
def perform (fun, arg):
return fun(arg)
def a(n):
return n + 2
def d(n):
return n / 2.
def m(n):
return n * 2
def p(n):
return n ** 2
def r(n):
return n ** 0.5
def s(n):
return n - 2
if __name__ == "__main__":
from itertools import permutations
for i, perm in enumerate(permutations([a, d, m, p, r, s])):
try:
k = perform(perm[5], perform(perm[4], perform(perm[3], perform(perm[2], perform(perm[1], perform(perm[0], 0))))))
except ValueError:
k = float('nan')
print "%03d. %s: %8.8f" % (i + 1, ''.join([x.__name__ for x in perm]), k)
The line that doesn't seem right to me is the one with nested perform calls: k = perform(...perform(...(. What I need to do is apply the first function in the permutation tuple to 0, and then that function's result to the second function in the tuple, and so on through the permutation tuple until I come up with the ultimate result of applying the component functions.
Is there a cleaner way to successively apply the functions in perm to the corresponding results, starting with 0 as an argument? I've toyed with map and recursion, but I haven't been able to hit upon a solution any more elegant than the one above.

Why not simply:
x = init value
for f in funclist:
x = f(x)
or in a bit fancier way:
value = reduce(lambda x, f: f(x), funclist, init_value)

Related

Converting a function with two recursive calls into an interative function

I've got a function that has two recursive calls and I'm trying to convert it to an iterative function. I've got it figured out where I can do it with one call fairly easily, but I can't figure out how to incorporate the other call.
the function:
def specialMultiplication(n):
if n < 2:
return 1
return n * specialMultiplication(n-1) * specialMultiplication(n-2)
If I just had one of them, it would be really easily:
def specialMult(n, mult = 1):
while n > 1:
(n, mult) = (n-1, n * mult) # Or n-2 for the second one
return mult
I just can't figure out how to add the second call in to get the right answer overall. Thanks!
If you don't mind changing the structure of your algorithm a bit more, you can calculate the values in a bottom-up fashion, starting with the smallest values.
def specialMultiplication(max_n):
a = b = 1
for n in range(1, max_n+1):
a, b = b, a*b*n
return b
Convert the recursion to an iterative function using an auxiliary "todo list":
def specialMultiplication(n):
to_process = []
result = 1
if n >= 2:
to_process.append(n)
while to_process: # while list is not empty
n = to_process.pop()
result *= n
if n >= 3:
to_process.append(n-1)
if n >= 4:
to_process.append(n-2)
return result
create a work list (to_process)
if n >= 2, add n to the list
while to_process is not empty, pop item from list, multiply to result
if n-1 < 2, don't perform "left" operation (don't append to work list)
if n-2 < 2, don't perform "right" operation (don't append to work list)
This method has the advantage of consuming less stack. I've checked the results against recursive version for values from 1 to 25 and they were equal.
Note that it's still slow, since complexity is O(2^n) so it's beginning to be really slow from n=30 (time doubles when n increases by 1). n=28 is computed in 12 seconds on my laptop.
I've successfully used this method to fix a stack overflow problem when performing a flood fill algorithm: Fatal Python error: Cannot recover from stack overflow. During Flood Fill but here Blcknght answer is more adapted because it rethinks the way of computing it from the start.
The OP's function has the same recursive structure as the Fibonacci and Lucas functions, just with different values for f0, f1, and g:
f(0) = f0
f(1) = f1
f(n) = g(f(n-2), f(n-1), n)
This is an example of a recurrence relation. Here is an iterative version of the general solution that calculates f(n) in n steps. It corresponds to a bottom-up tail recursion.
def f(n):
if not isinstance(n, int): # Can be loosened a bit
raise TypeError('Input must be an int') # Can be more informative
if n < 0:
raise ValueError('Input must be non-negative')
if n == 0:
return f0
i, fi_1, fi = 1, f0, f1 # invariant: fi_1, fi = f(i-1), f(i)
while i < n:
i += 1
fi_1, fi = fi, g(fi_1, fi, n) # restore invariant for new i
return fi
Blckknight's answer is a simplified version of this

Converting a for loop to recursion in Python [duplicate]

This question already has answers here:
Generating permutations with repetitions
(6 answers)
Closed 3 months ago.
I am new to recursion and am trying to convert a for loop to recursion.
allProducts = []
for a in range(10):
for b in range(10):
for c in range(10):
for d in range(10):
if (a*b*c*d)%6==0:
allProducts.append(a*b*c*d)
I am not able to convert this to a recursive program, which means I am not able to scale up. The idea is this - define a recursive program in Python that takes input A (number of for loops) and B (number which is a divisor of the product).
Any help would be really helpful.
You can use itertools.product and its repeat argument:
from operator import mul
import itertools
def myprod(n, div, repeat=4):
# i is a list of factors
for i in itertools.product(range(n), repeat=repeat):
# calculate product of all elements of list
prod = reduce(mul, i, 1)
if prod % div == 0:
yield prod
print list(myprod(10, 6))
Changing the repeat argument of myprod will change the number of loops and factors you are calculating.
Also, since multiplication is commutative (a * b == b * a) you should eliminate repetitive computations using itertools.combinations_with_replacement:
from operator import mul
import itertools
def myprod_unique(n, div, repeat=4):
for i in itertools.combinations_with_replacement(range(n), r=repeat):
prod = reduce(mul, i, 1)
if prod % div == 0:
yield prod
print list(myprod_unique(10, 6))
If you remove duplicate results from myprod using set you will find that the two results are equal:
print set(myprod_unique(10, 6)) == set(myprod(10, 6))
but you have cut down the number of operations drastically from n ** r to (n+r-1)! / r! / (n-1)!. For example 92,378 instead of 10,000,000,000 for n=10, r=10.
You should have a look at the builtin package itertools:
for a,b,c,d in itertools.product(range(10),range(10),range(10),range(10)):
if (a*b*c*d)%6==0:
allProducts.append(a*b*c*d)
The other answers don't use recursion. For completeness, here is one that does.
Recursive functions usually have two parts.
Base case: Handled directly. Corresponds to the body of the innermost loop in your code.
Recursive case: Involves calling the function again, corresponds to one for loop in your code.
Sometimes extra parameters need to be introduced. In this example the variables a, b, etc. that have already been set need to be passed in. The natural way to do that is using a list.
allProducts = []
def product(vals):
res = 1
for val in vals:
res *= val
return res
# n is the number of for loops remaining
# mod is the divisor
# vals is a list where the values of a, b, c, etc. will be placed
# start this function off using run(4, 6, [])
def run(n, mod, vals):
if n == 0:
# base case
if product(vals) % mod == 0:
allProducts.append(product(vals))
else:
# recursive case
for i in range(10):
# i takes the role of a, b, c, etc.
# add i to the vals
vals.append(i)
# recursively iterate over remaining n-1 variables
run(n - 1, mod, vals)
# remove i
vals.pop()

How do I make a function that can calculate something simple if it has an extra argument?

I have a simple function that just multiplies some numbers. It's pretty neat.
def multiply(a, b ,c):
"""Just some numbers I want to multiply. Lousy description.
"""
multiply = a * b * c
return multiply
print (multiply(2, 5, 6))
And the answer, 60, comes out. Pretty straightforward.
But let's say I wanted to multiply four numbers, a, b, c and d, this time. Would I have to repeat what I've written above, but add a "d"? Is there a simpler way?
def multiply(*nums):
num = 1
for i in nums:
num *= i
return num
multiply(1,2,3,4) #return 64
The * before nums means that this function takes any number of arguments that are going to be stored as a list in the variable nums. Then you can simply iterate through them and get the result.
I have written it in python 2, but the concept remains the same.
def multiply(a,b,c):
multiply = a*b*c
return multiply
def multiply_args(*args):
multiply = 1
for arg in args:
multiply = multiply * arg
return multiply
print multiply(2,5,6)
print multiply_args(2,5,6,2)
A link for understanding *args and **kwargs: http://pythontips.com/2013/08/04/args-and-kwargs-in-python-explained/

How does reduce function work?

As far as I understand, the reduce function takes a list l and a function f. Then, it calls the function f on first two elements of the list and then repeatedly calls the function f with the next list element and the previous result.
So, I define the following functions:
The following function computes the factorial.
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return fact(x) * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
Now, shouldn't this give me ((1! * 3!) * 1!) = 6? But, instead it gives 720. Why 720? It seems to take the factorial of 6 too. But, I need to understand why.
Can someone explains why this happens and a work-around?
I basically want to compute the product of factorials of all the entries in the list.
The backup plan is to run a loop and compute it. But, I would prefer using reduce.
The other answers are great. I'll simply add an illustrated example that I find pretty good to understand reduce():
>>> reduce(lambda x,y: x+y, [47,11,42,13])
113
will be computed as follows:
(Source) (mirror)
The easiest way to understand reduce() is to look at its pure Python equivalent code:
def myreduce(func, iterable, start=None):
it = iter(iterable)
if start is None:
try:
start = next(it)
except StopIteration:
raise TypeError('reduce() of empty sequence with no initial value')
accum_value = start
for x in iterable:
accum_value = func(accum_value, x)
return accum_value
You can see that it only makes sense for your reduce_func() to apply the factorial to the rightmost argument:
def fact(n):
if n == 0 or n == 1:
return 1
return fact(n-1) * n
def reduce_func(x,y):
return x * fact(y)
lst = [1, 3, 1]
print reduce(reduce_func, lst)
With that small revision, the code produces 6 as you expected :-)
Your function calls fact() on both arguments. You are calculating ((1! * 3!)! * 1!). The workaround is to only call it on only the second argument, and pass reduce() an initial value of 1.
From the Python reduce documentation,
reduce(function, sequence) returns a single value constructed by calling the (binary) function on the first two items of the sequence, then on the result and the next item, and so on.
So, stepping through. It computes reduce_func of the first two elements, reduce_func(1, 3) = 1! * 3! = 6. Then, it computes reduce_func of the result and the next item: reduce_func(6, 1) = 6! * 1! = 720.
You missed that, when the result of the first reduce_func call is passed as input to the second, it's factorialized before the multiplication.
Ok, got it:
I need to map the numbers to their factorials first and then call reduce with multiply operator.
So, this would work:
lst_fact = map(fact, lst)
reduce(operator.mul, lst_fact)
You could also implement factorial using reduce.
def factorial(n):
return(reduce(lambda x,y:x*y,range(n+1)[1:]))
Beyond the trivial examples, here is one where I find reduce to be actually quite useful:
Imagine an iterable of ordered int values, often with some runs of contiguous values, and that we'd like to "summarize" it as a list of tuples representing ranges. (Note also that this iterable could be a generator of a very long sequence --another reason to use reduce and not some operation on an in-memory collection).
from functools import reduce
def rle(a, b):
if a and a[-1][1] == b:
return a[:-1] + [(a[-1][0], b + 1)]
return a + [(b, b + 1)]
reduce(rle, [0, 1, 2, 5, 8, 9], [])
# [(0, 3), (5, 6), (8, 10)]
Notice the use of a proper initial value ([] here) for reduce.
Corner cases handled as well:
reduce(rle, [], [])
# []
reduce(rle, [0], [])
# [(0, 1)]
Well, first of all, your reduce_func doesn't have the structure of a fold; it doesn't match your description of a fold (which is correct).
The structure of a fold is: def foldl(func, start, iter): return func(start, foldl(func, next(iter), iter)
Now, your fact function doesn't operate on two elements - it just calculates factorial.
So, in sum, you're not using a fold, and with that definition of factorial, you don't need to.
If you do want to play around with factorial, check out the y-combinator: http://mvanier.livejournal.com/2897.html
If you want to learn about folds, look at my answer to this question, which demonstrates its use to calculate cumulative fractions: creating cumulative percentage from a dictionary of data
Reduce executes the function in parameter#1 successively through the values provided by the iterator in parameter#2
print '-------------- Example: Reduce(x + y) --------------'
def add(x,y): return x+y
x = 5
y = 10
import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot
def myreduce(a,b):
tot = 0
for i in range(a,b):
tot = tot+i
print i,tot
print 'myreduce('+str(a)+','+str(b)+')=' ,tot
myreduce(x,y)
print '-------------- Example: Reduce(x * y) --------------'
def add(x,y): return x*y
x = 5
y = 10
import functools
tot = functools.reduce(add, range(5, 10))
print 'reduce('+str(x)+','+str(y)+')=' ,tot
def myreduce(a,b):
tot = 1
for i in range(a,b):
tot = tot * i
print i,tot
print 'myreduce('+str(a)+','+str(b)+')=' ,tot
myreduce(x,y)

Pythonic way to implement three similar integer range operators?

I am working on a circular problem. In this problem, we have objects that are put on a ring of size MAX, and are assigned IDs from (0 to MAX-1).
I have three simple functions to test for range inclusions. inRange(i,j,k) tests if i is in the circular interval [j,k[ (Mnemonic is i inRange(j,k)). And I have the same for ranges ]j,k[ and ]j,k].
Code in those three methods look duplicated from one method to another:
def inRange(i,j,k):
"""
Returns True if i in [j, k[
* 0 <= i, j, k < MAX
* no order is assumed between j and k: we can have k < j
"""
if j <= k:
return j <= i < k
# j > k :
return j <= i or i < k
def inStrictRange(i,j,k):
"""
Returns True if i in ]j, k[
* 0 <= i, j, k < MAX
* no order is assumed between j and k: we can have k < j
"""
if j <= k:
return j < i < k
# j > k :
return j < i or i < k
def inRange2(i,j,k):
"""
Returns True if i in ]j, k]
* 0 <= i, j, k < MAX
* no order is assumed between j and k: we can have k < j
"""
if j <= k:
return j < i <= k
# j > k :
return j < i or i <= k
Do you know any cleaner way to implement those three methods? After all, only the operators are changing?!
After thinking of a better solution, I came up with:
from operator import lt, le
def _compare(i,j,k, op1, op2):
if j <= k:
return op1(j,i) and op2(i,k)
return op1(j,i) or op2(i,k)
def inRange(i,j,k):
return _compare(i,j,k, le, lt)
def inStrictRange(i,j,k):
return _compare(i,j,k, lt, lt)
def inRange2(i,j,k):
return _compare(i,j,k, lt, le)
Is it any better? Can you come up with something more intuitive?
In short, what would be the Pythonic way to write these three operators?
Also, I hate the inRange, inStrictRange, inRange2 names, but I can't think of crystal-clear names. Any ideas?
Thanks.
Two Zen of Python principles leap to mind:
Simple is better than complex.
There should be one—and preferably only one—obvious way to do it.
range
The Python built-in function range(start, end) generates a list from start to end.1 The first element of that list is start, and the last element is end - 1.
There is no range_strict function or inclusive_range function. This was very awkward to me when I started in Python. ("I just want a list from a to b inclusive! How hard is that, Guido?") However, the convention used in calling the range function was simple and easy to remember, and the lack of multiple functions made it easy to remember exactly how to generate a range every time.
Recommendation
As you've probably guessed, my recommendation is to only create a function to test whether i is in the range [j, k). In fact, my recommendation is to keep only your existing inRange function.
(Since your question specifically mentions Pythonicity, I would recommend you name the function as in_range to better fit with the Python Style Guide.)
Justification
Why is this a good idea?
The single function is easy to understand. It is very easy to learn how to use it.
Of course, the same could be said for each of your three starting functions. So far so good.
There is only one function to learn. There are not three functions with necessarily similar names.
Given the similar names and behaviours of your three functions, it is somewhat possible that you will, at some point, use the wrong function. This is compounded by the fact that the functions return the same value except for edge cases, which could lead to a hard-to-find off-by-one bug. By only making one function available, you know you will not make such a mistake.
The function is easy to edit.
It is unlikely that you'll need to ever debug or edit such an easy piece of code. However, should you need to do so, you need only edit this one function. With your original three functions, you have to make the same edit in three places. With your revised code in your self-answer, the code is made slightly less intuitive by the operator obfuscation.
The "size" of the range is obvious.
For a given ring where you would use inRange(i, j, k), it is obvious how many elements would be covered by the range [j, k). Here it is in code.
if j <= k:
size = k - j
if j > k:
size = k - j + MAX
So therefore
size = (k - j) % MAX
Caveats
I'm approaching this problem from a completely generic point of view, such as that of a person writing a function for a publicly-released library. Since I don't know your problem domain, I can't say whether this is a practical solution.
Using this solution may mean a fair bit of refactoring of the code that calls these functions. Look through this code to see if editing it is prohibitively difficult or tedious.
1: Actually, it is range([start], end, [step]). I trust you get what I mean though.
The Pythonic way to do it is to choose readability, and therefor keep the 3 methods as they were at the beginning.
It's not like they are HUGE methods, or there are thousand of them, or you would have to dynamically generate them.
No higher-order functions, but it's less code, even with the extraneous else.
def exclusive(i, j, k):
if j <= k:
return j < i < k
else:
return j < i or i < k
def inclusive_left(i, j, k):
return i==j or exclusive(i, j, k)
def inclusive_right(i, j, k):
return i==k or exclusive(i, j, k)
I actually tried switching the identifiers to n, a, b, but the code began to look less cohesive. (My point: perfecting this code may not be a productive use of time.)
Now I am thinking of something such as:
def comparator(lop, rop):
def comp(i, j, k):
if j <= k:
return lop(j, i) and rop(i,k)
return lop(j, i) or rop(i,k)
return comp
from operator import le, lt
inRange = comparator(le, lt)
inStrictRange = comparator(lt, lt)
inRange2 = comparator(lt, le)
Which looks better indeed.
I certainly agree that you need only one function, and that the function should use a (Pythonic) half-open range.
Two suggestions:
Use meaningful names for the args:
in_range(x, lo, hi) is a big
improvement relative to the
2-keystroke cost.
Document the fact that the
constraint hi < MAX means that it is
not possible to express a range that
includes all MAX elements. As
Wesley remarked, size = (k - j) %
MAX i.e. size = (hi - lo) % MAX
and thus 0 <= size < MAX.
To make it more familiar to your users, I would have one main in_range function with the same bounds as range(). This makes it much easier to remember, and has other nice properties as Wesley mentioned.
def in_range(i, j, k):
return (j <= i < k) if j <= k else (j <= i or i < k)
You can certainly use this one alone for all your use cases by adding 1 to j and/or k. If you find that you're using a specific form frequently, then you can define it in terms of the main one:
def exclusive(i, j, k):
"""Excludes both endpoints."""
return in_range(i, j + 1, k)
def inclusive(i, j, k):
"""Includes both endpoints."""
return in_range(i, j, k + 1)
def weird(i, j, k):
"""Excludes the left endpoint but includes the right endpoint."""
return in_range(i, j + 1, k + 1)
This is shorter than mucking around with operators, and is also much less confusing to understand. Also, note that you should use underscores instead of camelCase for function names in Python.
I'd go one step further than Wesley in aping the normal python 'in range' idiom; i'd write a cyclic_range class:
import itertools
MAX = 10 # or whatever
class cyclic_range(object):
def __init__(self, start, stop):
# mod so you can be a bit sloppy with indices, plus -1 means the last element, as with list indices
self.start = start % MAX
self.stop = stop % MAX
def __len__(self):
return (self.stop - self.start) % MAX
def __getitem__(self, i):
return (self.start + i) % MAX
def __contains__(self, x):
if (self.start < self.stop):
return (x >= self.start) and (x < self.stop)
else:
return (x >= self.start) or (x < self.stop)
def __iter__(self):
for i in xrange(len(self)):
yield self[i]
def __eq__(self, other):
if (len(self) != len(other)): return False
for a, b in itertools.izip(self, other):
if (a != b): return False
return True
def __hash__(self):
return (self.start << 1) + self.stop
def __str__(self):
return str(list(self))
def __repr__(self):
return "cyclic_range(" + str(self.start) + ", " + str(self.stop) + ")"
# and whatever other list-like methods you fancy
You can then write code like:
if (myIndex in cyclic_range(firstNode, stopNode)):
blah
To do the equivalent of inRange. To do inStrictRange, write:
if (myIndex in cyclic_range(firstNode + 1, stopNode)):
And to do inRange2:
if (myIndex in cyclic_range(firstNode + 1, stopNode + 1)):
If you don't like doing the additions by hand, how about adding these methods:
def strict(self):
return cyclic_range(self.start + 1, self.stop)
def right_closed(self):
return cyclic_range(self.start + 1, self.stop + 1)
And then doing:
if (myIndex in cyclic_range(firstNode, stopNode).strict()): # inStrictRange
if (myIndex in cyclic_range(firstNode, stopNode).closed_right()): # inRange2
Whilst this approach is, IMHO, more readable, it does involve doing an allocation, rather than just a function call, which is more expensive - although still O(1). But then if you really cared about performance, you wouldn't be using python!

Categories

Resources