Next Chapter: Finite State Machine in Python

## Currying

### General Idea

In mathematics and computer science, currying is the technique of breaking down the evaluation of a function that takes multiple arguments into evaluating a sequence of single-argument functions.

Currying is not only used in programming but in theoretical computer science as well. The reason is that it is often easier to transform multiple argument models into single argument models.

The need for currying arises for example in the following case: Let us assume that we have a context, in which we can only use a function with one argument. We have to use a function with multiple parameters. So we need a way to transform this function into a function with just one parameter. Currying provides the solution to this problem. Currying means rearranging a multiple-parameter function into a chain of functions applied to one argument. It is always possible to transform a function with multiple arguments into a chain of single-argument functions.

Python is not equipped for this programming style. This means there are no special syntactical constructs available to support currying. On the other hand, Python is well suited to simulate this way of programming. We will introduce various ways to accomplish this in this chapter of our tutorial.

Some readers not interested in the mathematical details can skip the following two subchapters, because the a mathematically focussed.

### Composition of Functions

We define the composition h of two function f and g

$$h(x) = g(f(x)$$

often written as

$$h = (g \circ f)(x)$$

in the following Python example.

The composition of two functions is a chaining process in which the output of the inner function becomes the input of the outer function.

### Currying

As we have already metnioned in the introduction, currying means transforming a function with multiple parameters into a chain of functions with one parameter.

We will start with the simplest case, i.e. two parameters. Given is a function $f$ with two parameters $x$ and $y$. We can curry the function in the following way:

We have to find a function $g$ which returns a function $h$ when it is applied to the second parameter $y$ of $f$. $h$ is a function which can be applied to the first parameter $x$ of $f$, satisfying the condition

$$f(x, y) = g(y)(x) = h(x)$$

Now we have a look at the general case of currying. Let us assume that we have a function $f$ with $n$ parameters:

$$f(x_1, x_2, \dots x_n)$$

Currying leads to a cascade of functions:

$$f_{n-1} = f_n(x_n)$$ $$...$$ $$f_1 = f_2(x_2)$$ $$f(x_1, x_2, \dots x_n) = f_1(x_1)$$

### Composition of Functions in Python

#### Two Functions

The function *compose* can be used to create to compose two functions:

def compose(g, f): def h(x): return g(f(x)) return h

We will use our compose function in the next example. Let's assume, we have a thermometer, which is not working accurate. The correct temperature can be calculated by applying the function *readjust* to the temperature values. Let us further assume that we have to convert our temperature values into degrees fahrenheit. We can do this by applying *compose* to both functions:

def celsius2fahrenheit(t): return 1.8 * t + 32 def readjust(t): return 0.9 * t - 0.5 convert = compose(readjust, celsius2fahrenheit) convert(10), celsius2fahrenheit(10)The previous code returned the following output:

(44.5, 50.0)

The composition of two functions is generally not commutative, i.e. compose(celsius2fahrenheit, readjust) is different from compose(readjust, celsius2fahrenheit)

convert2 = compose(celsius2fahrenheit, readjust) convert2(10), celsius2fahrenheit(10)The above Python code returned the following output:

(47.3, 50.0)

*convert2* is not a solution to our problem, because it is not readjusting the original temperatures of our thermometer but the transformed Fahrenheit values!

#### "compose" with an Arbitrary Number of Arguments

The function *compose* which we have just defined can only cope with single-argument functions. We can generalize our function *compose* so that it can cope with all possible functions. This is not currying of course but nevertheless also an interesting function.

def compose(g, f): def h(*args, **kwargs): return g(f(*args, **kwargs)) return h

Example using a function with two parmameters.

def BMI(weight, height): return weight / height**2 def evaluate_BMI(bmi): if bmi < 15: return "Very severely underweight" elif bmi < 16: return "Severely underweight" elif bmi < 18.5: return "Underweight" elif bmi < 25: return "Normal (healthy weight)" elif bmi < 30: return "Overweight" elif bmi < 35: return "Obese Class I (Moderately obese)" elif bmi < 40: return "Obese Class II (Severely obese)" else: return "Obese Class III (Very severely obese)" f = compose(evaluate_BMI, BMI) weight = 1 while weight > 0: weight = float(input("weight (kg) ")) height = float(input("height (m) ")) print(f(weight, height))

weight (kg) 70 height (m) 1.76 Normal (healthy weight) weight (kg) 0 height (m) 1 Very severely underweight

### BMI Chart

This is off-topic, because it is not about currying. Yet, it is nice to have a BMI chart, especially if it is created with Python means. We use contour plots from the Matplotlib module in our program. If you want to learn more about contour plots, you can go to chapter on Contour Plots of our Matplotlib tutorial.

%matplotlib inline import matplotlib.pyplot as plt import pylab as pl xlist = pl.linspace(1.2, 2.0, 50) ylist = pl.linspace(50, 100, 50) X, Y = pl.meshgrid(xlist, ylist) Z = Y / (X**2) plt.figure() levels = [0, 15, 16, 18.5, 25, 30, 35, 40, 100] cp = plt.contour(X, Y, Z, levels) pl.clabel(cp, colors = 'k', fmt = '%2.1f', fontsize=12) c = ('#0000FF', '#0020AA', '#008060', '#00AA40', '#00FF00', '#40AA00', '#992200', '#FF0000') cp = plt.contourf(X, Y, Z, levels, colors=c) plt.colorbar(cp) plt.title('Contour Plot') plt.xlabel('x (m)') plt.ylabel('y (kg)') plt.show()

### Currying Examples in Python

#### Currying BMI

We used the function BMI in a composition in the previous example. We will now use it as a first currying example. The height of grown-ups is principally a constant. Okay, I know, we shrink every day from dawn to dusk about one centimetre and we get the loss returned over night. We define a function *f* which takes a height and returns a function whith one parameter (weight) to return the BMI:

def BMI_weight(height): def h(weight): return weight / height**2 return h for weight in [60, 68, 74]: for height in [164, 168, 172]: print(BMI_weight(height)(weight), BMI(weight, height))

0.00223081499107674 0.00223081499107674 0.0021258503401360546 0.0021258503401360546 0.0020281233098972417 0.0020281233098972417 0.002528256989886972 0.002528256989886972 0.002409297052154195 0.002409297052154195 0.002298539751216874 0.002298539751216874 0.002751338488994646 0.002751338488994646 0.0026218820861678006 0.0026218820861678006 0.002501352082206598 0.002501352082206598

#### Example: Currency Conversion

In the chapter on Magic Functions of our tutorial we had an excercise, in which we defined a class for currency conversions.

We will define now a function *exchange*, which takes three arguments:

- The source currency
- The target currency
- The amount in the source currency

To function needs the actual exchange rates. We can download them from finance.yahoo.com website with the function get_currencies. Though in our example we use some old exchange rates:

currencies = {'CHF': 1.0821202355817312, 'CAD': 1.488609845538393, 'GBP': 0.8916546282920325, 'JPY': 114.38826536281809, 'EUR': 1.0, 'USD': 1.11123458162018}

def exchange(from_currency, to_currency, amount): result = amount * currencies[to_currency] result /= currencies[from_currency] return result exchange("CHF", "CAD", 100)We received the following output:

137.56418155678784

We can now define *curried* functions from the function *exchange*:

def exchange_from_CHF(to_currency, amount): return exchange("CHF", to_currency, amount) def CHF2EUR(amount): return exchange_from_CHF("EUR", amount) print(exchange_from_CHF("EUR", 90)) print(CHF2EUR(90))

83.17005545286507 83.17005545286507

We want to rewrite the function *exchange* in a curryable version:

def curry_exchange(from_currency=None, to_currency=None, amount=None): if from_currency: if to_currency: if amount: def f(): return exchange(from_currency, to_currency, amount) else: def f(amount): return exchange(from_currency, to_currency, amount) else: if amount: def f(to_currency): return exchange(from_currency, to_currency, amount) else: def f(to_currency=None, amount=None): if amount: if to_currency: def h(): return exchange(from_currency, to_currency, amount) else: def h(to_currency): if to_currency: return exchange(from_currency, to_currency, amount) else: if to_currency: def h(amount): return exchange(from_currency, to_currency, amount) else: def h(to_currency, amount): return exchange(from_currency, to_currency, amount) return h else: def f(from_currency, to_currency, amount): return exchange(from_currency, to_currency, amount) return f

We can redefine *exchange_from_CHF* and *CHF2EUR* in a properly curried way:

exchange_from_CHF = curry_exchange("CHF") print(exchange_from_CHF("EUR", 90)) CHF2EUR = curry_exchange("CHF", "EUR") print(CHF2EUR(90))

<function curry_exchange.<locals>.f.<locals>.h at 0x7f7543673268> 83.17005545286507

You will find various calls to curry_exchange in the following examples:

print(curry_exchange("CHF")( "EUR", 100)) print(curry_exchange("CHF", "EUR")(100)) f = curry_exchange("CHF") print(f("EUR", 100)) g = f("EUR") print(g(100)) CHF2EUR= curry_exchange("CHF", "EUR") print(CHF2EUR(100)) k = curry_exchange("CHF", "EUR", 100) print(k()) print(curry_exchange("CHF", "EUR", 100)) f = curry_exchange("CHF")(amount=100) print(f("EUR")) f = curry_exchange("CHF") print(f("EUR", 100)) f = curry_exchange("CHF") g = f("EUR") print(g(100)) g2 = f(amount=120) for currency in currencies: print(currency, g2(currency))

<function curry_exchange.<locals>.f.<locals>.h at 0x7f7543438950> 92.41117272540563 <function curry_exchange.<locals>.f.<locals>.h at 0x7f754372a730> 92.41117272540563 92.41117272540563 92.41117272540563 <function curry_exchange.<locals>.f at 0x7f7543438840> 92.41117272540563 <function curry_exchange.<locals>.f.<locals>.h at 0x7f7543673268> 92.41117272540563 CHF 120.00000000000001 CAD 165.07701786814542 GBP 98.87861983980284 JPY 12684.9044978435 EUR 110.89340727048676 USD 123.22858903265559

So far we have written custom-made curry functions. We will define a general currying function in the following chapter of our tutorial.

### General Currying

def arimean(*args): return sum(args) / len(args) def curry(func): f_args = [] f_kwargs = {} def f(*args, **kwargs): nonlocal f_args, f_kwargs if args or kwargs: f_args += args f_kwargs.update(kwargs) return f else: return func(*f_args, **f_kwargs) return f s = curry(arimean) s(2)(5)(9)(4, 5) s(5, 9) print(s()) s2 = curry(arimean) s2(2)(500)(9)(4, 5) s2(5, 9) s2() def add(x, y, z): return x + y + z s2 = curry(add) s2(3, 5)(8)()

5.571428571428571The previous Python code returned the following output:

16

def exchange(from_currency, to_currency, amount): result = amount * currencies[to_currency] result /= currencies[from_currency] return result e = curry(exchange) print(e) f = e("CHF", "EUR") print(f(10)()) e2 = curry(exchange) f = e2(to_currency="USD", amount=100, from_currency="CHF") f() #print(f(from_currency="CHF")())

<function curry.<locals>.f at 0x7f7543702bf8> 9.241117272540563The code above returned the following:

102.69049086054633

def arimean(*args): return sum(args) / len(args) def curry(func): f_args = [] f_kwargs = {} def f(*args, **kwargs): nonlocal f_args, f_kwargs if args or kwargs: f_args += args f_kwargs.update(kwargs) try: return func(*f_args, **f_kwargs) except TypeError: return f else: return func(*f_args, **f_kwargs) return f s = curry(arimean) s(2, 4, 5) e = curry(exchange) print(e) f = e("CHF", "EUR") print(f(10)) e2 = curry(exchange) f = e2(to_currency="USD", amount=100) f("CHF")

<function curry.<locals>.f at 0x7f7543457048> 9.241117272540563The above Python code returned the following output:

102.69049086054633

**partial**-Function

The function *partial* from the module *functools* can be used to simulate currying as well. It can also be used to "freeze" some of the function's arguments. This means it simplifies the functions signature.

We use it now to curry our function exchange once more in a different way:

from functools import partial f = partial(exchange, to_currency="USD", amount=100) f("CHF") f = partial(f, "CHF") f()The above Python code returned the following result:

102.69049086054633

Next Chapter: Finite State Machine in Python