def pfa(f, *p):
return lambda *q: f(*(p + q))
Partial function application is where you fix particular parameters of a function, producing a new function with the fixed parameters substituted in. For example, say I had a function add(a,b) = a + b. I can use partial function to 'fix' the first argument, a, to a particular number x, and this produces a new function add(b) = x + b.
In practice, PFA is often a more compact and readable version of the ugly anonymous functions you give to higher-order functions such as lambda. For example, consider the following code extract which transforms [a,b,c,...] to [2^a, 2^b, 2^c, ...]:
map(lambda n: pow(2, n), list)
Now take a look at how it is done with PFA:
map(PFA(pow, 2), list)
PFA in this instance creates a new function by substituting the value '2' as the first argument of the pow function, transforming pow(b, e) = b^e into pow(e) = 2^e.
Haskell's PFA notation is even more elegant; because everything in Haskell is just chained evaluation of partial functions, there's no special or unique notation for it, so the following is valid code:
map (2^) list
That maps the partial function 2^ onto list. Haskell also has lambdas, but they're no where near as short or understandable.
map (\x -> 2^x) list
The Python implementation above can also fix multiple arguments at one time, as in pfa(f(a,b,c), a, b) -> f(c). Sometimes it may be preferable to fix the second or third argument, while leaving the first argument variable -- Haskell deals with this elegantly as usual, but I haven't found a nice way to do it in Python.
note: apparently the functools module already has a partial() function for PFA. whoops :)
Function composition is another concept (again, originally mathematical) with similar roots. Here's an implementation:
def compose(*fs):
return lambda x: reduce(lambda a,b: b(a), (fs+x)[::-1])
Essentially, this takes two functions f(x) and g(y) and produces a new function h(y) == f(g(y)). The new function is often notated as (f.g)(y), as in "f.g is the composition of f and g." Like partial function application, it is rarely a necessity to use function composition, but again it can make things more compact and more intuitive.
A common construct I use when dealing with strings is as follows:
new_s = old_s.strip().split()
This can be composed as follows:
new_s = compose(str.split, str.strip)(old_s)
A better example:
hash(bin(bool(id(sum(range(10)))))) # becomes compose(hash, bin, bool, id, sum, range)(10)
No comments:
Post a Comment