## 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.f Currying is also used in theoretical computer science, because it is often easier to transform multiple argument models into single argument models.

### Composition of Functions

We define the composition h of two function f and g

$$h(x) = g(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.

```
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 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)
```

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

## Example Currency Conversion

In our chapter on Magic Functions we had an excercise on currency conversion.

## "compose" with Arbitrary Arguments

The function *compose* which we have just defined can only copy with single-argument functions. We can generalize our function *compose* so that it can cope with all possible functions.

```
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))
```

```
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()
```

```
```