patternpythonMinor
Recursive higher-order function
Viewed 0 times
recursiveorderfunctionhigher
Problem
I have implemented a
Since I am still quite new in python and want to learn it correctly, I would be happy about any feedback including style, variable naming...
A first question is how to name the function. Because of Mathematica's documentation I called it
An example:
through function for myself in a project.Since I am still quite new in python and want to learn it correctly, I would be happy about any feedback including style, variable naming...
A first question is how to name the function. Because of Mathematica's documentation I called it
through. Is there a more intuitive name?def through(list_of_functions, value, recurse_level=np.infty):
"""Calls each function in a list
This function is passing the value as argument to each function
in a list.
For a one dimensional list consisting of
only functions it is equivalent to::
[f(value) for f in list]
If an element is not callable and not iterable, it is not called.
An iterable is either directly appended or will be recursed through
depending on recurse_level.
Args:
list_of_functions (list):
value (any type):
recurse_level (int):
Returns:
list:
"""
new_list = []
for possible_function in list_of_functions:
try:
new_list.append(possible_function(value))
except TypeError: # item not callable; could be iterable
# If it is iterable and recurse_level is not negative
# recurse through elements
if recurse_level >= 0:
try:
new_list.append(through(possible_function,
value,
recurse_level))
recurse_level = recurse_level - 1
except TypeError: # not an iterable; append without calling
new_list.append(possible_function)
else:
new_list.append(possible_function)
return new_listAn example:
In: test = [[1, 2], [3, (lambda x : x ** 2)], (lambda x : x ** 3)]
In: through(test, 4, recurse_level=1)
Out: [[1, 2], [3, 16], 64]
In: through(test, 4, recurse_level=0)
Out: [[1, 2], [3, >], 64]Solution
Naming
Use Better Checks for Callable / Iterable
You check if something's callable by trying to call it and catching a
A similar situation exists in the way you check for iterables. In this case, you can use the
Consider Using a List Comprehension
Reading your code, I thought it was a bit ironic that you used a list comprehension in the docstring to describe the functions behavior, but not in the code itself. Granted, due to the recursive nature, it's not quite as straight-forward. But with just a little refactoring, it should be possible. Here's how I would do it:
I've omitted the recursion limit, but that should be trivial to add.
- In Clojure, there's a similar function named
juxt. Not saying that's a better name by any means, just mentioning what it happens to be called in one other place. Upon further reflection, what you are doing is really just a recursive form of function application. So a suitable name might reflect that. Perhapsapply_rec?
- Use the shortest name that conveys sufficient meaning. For example,
list_of_functionscould be shortened tofunctions(or even justfs).possible_functioncould be shortened tof. (Single-character names are not good names in general, but for generic entities that have a well-known mathematical notation, they are a good fit.)
Use Better Checks for Callable / Iterable
You check if something's callable by trying to call it and catching a
TypeError exception. However, if you call a function, and that function happens to through a TypeError, then your code will incorrectly assume it's not a function. You can use the callable function instead, to determine whether or not something is callable.A similar situation exists in the way you check for iterables. In this case, you can use the
iter function as a fail-fast way to check if something's iterable.Consider Using a List Comprehension
Reading your code, I thought it was a bit ironic that you used a list comprehension in the docstring to describe the functions behavior, but not in the code itself. Granted, due to the recursive nature, it's not quite as straight-forward. But with just a little refactoring, it should be possible. Here's how I would do it:
def apply_rec(f, x):
"""
If f is a function, applies f to x and returns the result.
Otherwise, if f is iterable, applies every element in f to x,
returning a list of the results.
If f is neither callable nor iterable, returns f.
"""
if callable(f):
return f(x)
else:
try:
fs = iter(f)
except:
# Not iterable => return f
return f
return [apply_rec(f, x) for f in fs]I've omitted the recursion limit, but that should be trivial to add.
Code Snippets
def apply_rec(f, x):
"""
If f is a function, applies f to x and returns the result.
Otherwise, if f is iterable, applies every element in f to x,
returning a list of the results.
If f is neither callable nor iterable, returns f.
"""
if callable(f):
return f(x)
else:
try:
fs = iter(f)
except:
# Not iterable => return f
return f
return [apply_rec(f, x) for f in fs]Context
StackExchange Code Review Q#147600, answer score: 5
Revisions (0)
No revisions yet.