Sunteți pe pagina 1din 49

Tom Moertels Blog

Home About Archive

Tricks of the trade:


Recursion to Iteration, Part 1: The Simple Method, secret
features, and accumulators
By Tom Moertel
Posted on May 11, 2013
Tags: programming, recursion, iteration, python, google code jam, puzzles, recursion-to-iteration
series

Alternative title: I wish Python had tail-call elimination.


Recursive programming is powerful because it maps so easily to proof by
induction, making it easy to design algorithms and prove them correct. Its
important because getting stuff right is what programming is all about.
But recursion is poorly supported by many popular programming languages. Drop a large input into a recursive algorithm in Python, and youll
probably hit the runtimes recursion limit. Raise the limit, and you may run
out of stack space and segfault.
These are not happy outcomes.

Therefore, an important trick of the trade is knowing how to translate recursive algorithms into iterative algorithms. That way, you can design,
prove, and initially code your algorithms in the almighty realm of recursion.
Then, after youve got things just the way you want them, you can translate
your algorithms into equivalent iterative forms through a series of mechanical steps. You can prove your cake and run it in Python, too.
This topic turning recursion into iteration is fascinating enough that
Im going to do a series of posts on it. Tail calls, trampolines, continuationpassing style and more. Lots of good stuff.
For today, though, lets just look at one simple method and one supporting
trick.

The Simple Method


This translation method works on many simple recursive functions. When
it works, it works well, and the results are lean and fast. I generally try it
first and consider more complicated methods only when it fails.
In a nutshell:
1. Study the function.
2. Convert all recursive calls into tail calls. (If you cant, stop. Try another
method.)
3. Introduce a one-shot loop around the function body.
4. Convert tail calls into continue statements.
5. Tidy up.
An important property of this method is that its incrementally correct
after every step you have a function thats equivalent to the original. So if
you have unit tests, you can run them after each and every step to make
sure you didnt make a mistake.

Lets see the method in action.

Example: factorial
With a function this simple, we could probably go straight to the iterative
version without using any techniques, just a little noggin power. But the
point here is to develop a mechanical process that we can trust when our
functions arent so simple or our noggins arent so powered. So were going
to work on a really simple function so that we can focus on the process.
Ready? Then lets show these guys how cyber-commandos get it done! Mark
IV style!
1. Study the original function.
def factorial(n):
if n < 2:
return 1
return n * factorial(n - 1)
Nothing scary here. Just one recursive call. We can do this!
2. Convert recursive calls into tail calls.
def factorial1a(n, acc=1):
if n < 2:
return 1 * acc
return factorial1a(n - 1, acc * n)
(If this step seemed confusing, see the Bonus Explainer at the end of
the article for the secret feature trick behind the step.)
3. Introduce a one-shot loop around the function body. You want
while True: body ; break .
def factorial1b(n, acc=1):
while True:
if n < 2:
return 1 * acc

return factorial1b(n - 1, acc * n)


break
Yes, I know putting a break after a return is crazy, but do it anyway.
Clean-up comes later. For now, were going by the numbers.
4. Replace all recursive tail calls f(x=x1, y=y1, ...) with
(x, y, ...) = (x1, y1, ...); continue . Be sure to update all arguments
in the assignment.
def factorial1c(n, acc=1):
while True:
if n < 2:
return 1 * acc
(n, acc) = (n - 1, acc * n)
continue
break
For this step, I copy the original functions argument list, parentheses
and all, and paste it over the return keyword. Less chance to screw
something up that way. Its all about being mechanical.
5. Tidy the code and make it more idiomatic.
def factorial1d(n, acc=1):
while n > 1:
(n, acc) = (n - 1, acc * n)
return acc
Okay. This step is less about mechanics and more about style. Eliminate the cruft. Simplify. Make it sparkle.
6. Thats it. Youre finished!

What have we gained?


We just did five steps of work to convert our original, recursive factorial
function into the equivalent, iterative factorial1d function. If our programming language had supported tail-call elimination, we could have

stopped at step two with factorial1a . But nooooooo We had to press on,
all the way through step five, because were using Python. Its almost
enough to make a cyber-commando punch a kitten.
No, the work wasnt hard, but it was still work. So what did it buy us?
To see what it bought us, lets look inside the Python run-time environment. Well use the Online Python Tutors visualizer to observe the buildup of stack frames as factorial , factorial1a , and factorial1d each compute the factorial of 5.
This is very cool, so dont miss the link: Visualize It! (ProTip: Open it in a
new tab.)
Click the Forward button to step through the execution of the functions.
Notice what happens in the Frames column. When factorial is computing
the factorial of 5, five frames build up on the stack. Not a coincidence.
Same thing for our tail-recursive factorial1a . (Youre right. It is tragic.)
But not for our iterative wonder factorial1d . It uses just one stack frame,
over and over, until its done. Thats economy!
Thats why we did the work. Economy. We converted O(n) stack use into O(1)
stack use. When n could be large, that savings matters. It could be the difference between getting an answer and getting a segfault.

Not-so-simple cases
Okay, so we tackled factorial . But that was an easy one. What if your function isnt so easy? Then its time for more advanced methods.
Thats our topic for next time.
Until then, keep your brain recursive and your Python code iterative.

Bonus Explainer: Using secret features for tail-call conversion


In step 2 of The Simple Method, we converted the recursive call in this code:
def factorial(n):
if n < 2:
return 1
return n * factorial(n - 1)

# <-- right here!

into the recursive tail call in this code:


def factorial(n, acc=1):
if n < 2:
return 1 * acc
return factorial(n - 1, acc * n)

# <-- oh, yeah!

This conversion is easy once you get the hang of it, but the first few times
you see it, it seems like magic. So lets take this one step by step.
First, the problem. We want to get rid of the n * in the following code:
return n * factorial(n - 1)
That n * stands between our recursive call to factorial and the return
keyword. In other words, the code is actually equivalent to the following:
x = factorial(n - 1)
result = n * x
return result
That is, our code has to call the factorial function, await its result ( x ), and
then do something with that result (multiply it by n ) before it can return its
result. Its that pesky intermediate doing something we must get rid of. We
want nothing but the recursive call to factorial in the return statement.
So how do we get rid of that multiplication?
Heres the trick. We extend our function with a multiplication feature and use

it to do the multiplication for us.


Shh! Its a secret feature, though, just for us. Nobody else.
In essence, every call to our extended function will not only compute a factorial but also (secretly) multiply that factorial by whatever extra value we give it.
The variables that hold these extra values are often called accumulators,
so I use the name acc here as a nod to tradition.
So heres our function, freshly extended:
def factorial(n, acc=1):
if n < 2:
return acc * 1
return acc * n * factorial(n - 1)
See what I did to add the secret multiplication feature? Two things.
First, I took the original function and added an additional argument acc ,
the multiplier. Note that it defaults to 1 so that it has no effect until we
give it some other value (since 1

x = x). Gotta keep the secret secret, after

all.
Second, I changed every single return statement from return {whatever} to
return acc * {whatever} . Whenever our function would have returned x , it
now returns acc * x . And thats it. Our secret feature is done! And its trivial to prove correct. (In fact, we just did prove it correct! Re-read the second
sentence.)
Both changes were entirely mechanical, hard to screw up, and, together,
default to doing nothing. These are the properties you want when adding
secret features to your functions.
Okay. Now we have a function that computes the factorial of n and, secretly, multiplies it by acc .
Now lets return to that troublesome line, but in our newly extended func-

tion:
return acc * n * factorial(n - 1)
It computes the factorial of n - 1 and then multiplies it by acc * n . But
wait! We dont need to do that multiplication ourselves. Not anymore. Now
we can ask our extended factorial function to do it for us, using the secret feature.
So we can rewrite the troublesome line as
return factorial(n - 1, acc * n)
And thats a tail call!
So our tail-call version of the factorial function is this:
def factorial(n, acc=1):
if n < 2:
return acc * 1
return factorial(n - 1, acc * n)
And now that all our recursive calls are tail calls there was only the one
this function is easy to convert into iterative form using The Simple Method
described in the main article.
Lets review the Secret Feature trick for making recursive calls into tail
calls. By the numbers:
1. Find a recursive call thats not a tail call.
2. Identify what work is being done between that call and its return
statement.
3. Extend the function with a secret feature to do that work, as controlled
by a new accumulator argument with a default value that causes it to do
nothing.
4. Use the secret feature to eliminate the old work.
5. Youve now got a tail call!

6. Repeat until all recursive calls are tail calls.


With a little practice, it becomes second nature. So

Exercise: Get your practice on!


You mission is to get rid of the recursion in the following function. Feel like
you can handle it? Then just fork the exercise repo and do your stuff to exercise1.py.
def find_val_or_next_smallest(bst, x):
"""Get the greatest value <= x in a binary search tree.
Returns None if no such value can be found.
"""
if bst is None:
return None
elif bst.val == x:
return x
elif bst.val > x:
return find_val_or_next_smallest(bst.left, x)
else:
right_best = find_val_or_next_smallest(bst.right, x)
if right_best is None:
return bst.val
return right_best
Have fun!

Site proudly generated by Hakyll

Tom Moertels Blog

Home About Archive

Tricks of the trade:


Recursion to Iteration, Part 2: Eliminating Recursion
with the Time-Traveling Secret Feature
Trick
By Tom Moertel
Posted on May 14, 2013
Tags: programming, recursion, iteration, python, google code jam, puzzles, recursion-to-iteration
series, tail calls

This is the second post in a series on converting recursive algorithms into


iterative algorithms. If you havent read the previous post, you probably
should. It introduces some terms and background that will be helpful.
Last time, if youll recall, we discussed The Simple Method of converting recursive functions into iterative functions. The method, as its name implies,
is straightforward and mostly mechanical. The only potential trouble is
that, for the method to work, you must convert all of the recursive calls in
your function into tail calls.

This task can be tricky. So we also discussed the Secret Feature trick for
putting recursive calls into tail-call form. That trick works well for simple
recursive calls, but when your calls arent so simple, you need a beefier version of the trick.
Thats the subject of this post: the Time-Traveling Secret Feature trick. Its
like sending a T-800 back in time to terminate a functions recursiveness
before it can ever offer resistance in the present.
Yeah.
But well have to work up to it. So stick with the early examples to prepare
for the cybernetic brain augmentations to come.
Enough talk! Lets start with a practical example.
Update 2013-06-17: If you want another example, heres also a step-by-step
conversion of the Fibonacci function.

Computing binomial coefficients


The binomial coefficient ( k ) for integers n and k gives the number of ways
n

to choose k items from a set of n items. For this reason, its often pro-

nounced n choose k . Its very common in combinatorics and statistics


and often pops up in the analysis of algorithms. A whole chapter, in fact, is
dedicated to them in Graham, Knuth, and Patashniks Concrete Mathematics.
Math textbooks define the coefficient like this:

n
n!
( k ) = k!(n k)! ,
but that form causes all sorts of problems for computers. Fortunately, Concrete Mathematics helpfully points out a lifesaving absorption identity:

n
n n1
=
,
(k)
(
)
k k1
and we know what that is, dont we? Thats a recursive function just waiting
to happen!
And that identity, along with the base case ( 0 )
n

= 1, gives us todays run-

ning example:

def binomial(n, k):


if k == 0:
return 1
return n * binomial(n - 1, k - 1) // k
Before going further, a cautionary point. Math math and computer math are
not the same. In math math, nx
k

n
x
k

x
k

n, but in computer math the

equation does not necessarily hold because of finite precision or, in our
case, because were dealing with integer division that throws away remainders. (By the way, in Python, // is the division operator that throws away
remainders.) For this reason, I have been careful to use the form
n * binomial(n - 1, k - 1) // k
instead of the more literal translation
(n // k) * binomial(n - 1, k - 1)
which, if you try it, youll see often produces the wrong answer.
Okay, our challenge is set before us. Ready to de-recursivify binomial ?

Once again, the Secret Feature trick


First, however, we must put the functions recursive call into tail-call form.
And you remember how to do that, right? With the Secret Feature trick, of
course! As a refresher from last time, heres the trick in a nutshell:

1. Find a recursive call thats not a tail call.


2. Identify what work is being done between that call and its return
statement.
3. Extend the function with a secret feature to do that work, as controlled
by a new accumulator argument with a default value that causes it to do
nothing.
4. Use the secret feature to eliminate the old work.
5. Youve now got a tail call!
6. Repeat until all recursive calls are tail calls.
Lets go through it, by the numbers.
1. Find a recursive call thats not a tail call.
Well, theres only three lines of logic. Were not talking rocket science
here.
def binomial(n, k):
if k == 0:
return 1
return n * binomial(n - 1, k - 1) // k
#
^^^^^^^^^^^^^^^^^^^^^^
#
right here!
2. Identify what work is being done between that call and its return statement.
In our minds eye, lets lift the recursive call out of the return statement and replace it with the variable x .
x = binomial(n - 1, k - 1)
return n * x // k
Now we can visualize the additional work as a separate function operating on x : multiplying it on the left by one number and dividing it on
the right by another:
def work(x, lmul, rdiv):
return lmul * x // rdiv

For functions this simple, you can just hold them in your head and inline them into your code as needed. But for more-complicated functions, it really does help to break them out like this. For this example,
Im going to pretend that our work function is more complicated, just
to show how to do it.
3. Extend the function with a secret feature to do that work, as controlled by a
new accumulator argument in this case a pair ( lmul , rdiv ) with a
default value that causes it to do nothing.
def work(x, lmul, rdiv):
return lmul * x // rdiv
def binomial(n, k, lmul=1, rdiv=1):
if k == 0:
return work(1, lmul, rdiv)
return work(n * binomial(n - 1, k - 1) // k, lmul, rdiv)
Note that I just mechanically converted all return {whatever} statements into return work({whatever}, lmul, rdiv) .
4. Use the secret feature to eliminate the old work.
Watch what happens to that final line.
def work(x, lmul, rdiv):
return lmul * x // rdiv
def binomial(n, k, lmul=1, rdiv=1):
if k == 0:
return work(1, lmul, rdiv)
return binomial(n - 1, k - 1, lmul * n, k * rdiv)
5. Youve now got a tail call!
Indeed, we do! All thats left is to inline the sole remaining work call:
def binomial(n, k, lmul=1, rdiv=1):
if k == 0:
return lmul * 1 // rdiv
return binomial(n - 1, k - 1, lmul * n, k * rdiv)

And to simplify away the needless multiplication by 1 in the return


statement:
def binomial(n, k, lmul=1, rdiv=1):
if k == 0:
return lmul // rdiv
return binomial(n - 1, k - 1, lmul * n, k * rdiv)
6. Repeat until all recursive calls are tail calls.
There was only one, so were done! Yay!
With all the recursive calls (just the one) converted into tail calls, we can
easily convert the function into iterative form by applying the Simple
Method. Heres what we get after we load the function body into a one-shot
loop and convert the tail call into an assignment-and- continue pair.
def binomial_iter(n, k, lmul=1, rdiv=1):
while True:
if k == 0:
return lmul // rdiv
(n, k, lmul, rdiv) = (n - 1, k - 1, lmul * n, k * rdiv)
continue
break
As a final touch, we can tidy up and in the process tuck the accumulators inside the function body to keep them completely secret:
def binomial_iter(n, k):
lmul = rdiv = 1
while k > 0:
(n, k, lmul, rdiv) = (n - 1, k - 1, lmul * n, k * rdiv)
return lmul // rdiv
And now we have an iterative version of our original binomial function!

A short, cautionary lesson


This next part is subtle but important. To understand whats going on, you

first need to see it. So, once again, lets use the Online Python Tutors excellent Python-runtime visualizer. Open the link below in a new tab if you can,
and then read on.
Visualize the execution of binomial(39, 9) .
Click the Forward button to advance through each step of the computation.
When you get to step 22, where the recursive version has fully loaded the
stack with its nested frames, click slowly. Watch the return value (in red)
carefully as you advance. See how it gently climbs to the final answer of
211915132, never exceeding that value?
Now continue stepping through to the iterative version. Watch the value of
lmul as you advance. See how it grows rapidly, finally reaching a whopping
76899763100160?
This difference matters. While both versions computed the correct answer,
the original recursive version would do so without exceeding the capacity of
32-bit signed integers.1 The iterative version, however, needs a comparatively hoggish 47 bits to faithfully arrive at the correct answer.
Pythons integers, lucky for us, grow as needed to hold their values, so we
need not fear overflow in this case, but not all languages for which we
might want to use our recursion-to-iteration techniques offer such protections. Its something to keep in mind the next time youre taking the recursion out of an algorithm in C:
typedef int32_t Int;
Int binomial(Int n, Int k) {
if (k == 0)
return 1;
return n * binomial(n - 1, k - 1) / k;
}
Int binomial_iter(Int n, Int k) {
Int lmul = 1, rdiv = 1;

while (k > 0) {
lmul *= n; rdiv *= k; n -= 1; k -= 1;
}
return lmul / rdiv;
}
int main(int argc, char* argv[]) {
printf("binomial(39, 9)
= %ld\n", (long) binomial(39, 9));
printf("binomial_iter(39, 9) = %ld\n", (long) binomial_iter(39, 9));
}
/* Output with Int = int32_t:
binomial(39, 9)
= 211915132
binomial_iter(39, 9) = -4481

<-- oops!

*/
In any case, bigger integers are slower and consume more memory. In one
important way, the original, recursive algorithm was better. We have lost
something important.
Now lets get it back.

The importance of respecting associativity and commutativity


It turns out that our iterative version of binomial is the originals evil twin.
Its stack usage looks charming and all, but something else about it is off.
Something hard to put our finger on. What is the difference?
It all comes down to grouping and ordering decisions. Implicit grouping and
ordering decisions that we overlooked during our code transformations.
Consider how both versions compute ( 2 ) .
5

First, the recursive version. We start out with n


sively expand the expression

= 5, k = 2 and then recur-

n(
until we hit the base case of k

n1
/k
)
k1

= 0 . The series of expansions proceeds like

so:

5
4
3
=
5
/2
=
5

4
(2)
(1)
( ( 0 )/1) /2 = 5 (4 (1)/1)/2 = 10.
At every step (except for the base case) we perform a multiplication by n

and then a division by k . Its that division by k that keeps intermediate results from getting out of hand.
Now lets look at the iterative version. Its not so obvious whats going on.
(Score another point for recursion over iteration!) But we can puzzle it out.

We start out with both accumulators at 1 and then loop over the decreasing
values of k , building up the accumulators until k

= 0 , at which point we re-

turn the quotient of the accumulators.


So the calculation is given by

5
lmul
((1) 5) 4
( 2 ) = rdiv = ((1) 2) 1 = 10.
Both the numerator and denominator grow and grow and grow until the final division. Not a good trend.
So why did the Secret Feature trick work great for factorial in our previous
article but fail us, albeit subtly, now? The answer is that in factorial the
extra work being done between each recursive call and its return statement
was multiplication and nothing more. And multiplication (of integers that
dont overflow) is commutative and associative. Meaning, ordering and
grouping dont matter.
The lesson, then, is this: Think carefully about whether ordering and
grouping matter before using the Secret Feature trick. If it matters, you

have two options: One, you can modify your algorithm so that ordering and
grouping dont matter and then use the Secret Feature trick. (But this option
is often intractable.) Two, you can use the Time-Traveling Secret Feature trick,
which preserves ordering and grouping. And that trick is what weve been
waiting for.
Its time.

The Time-Traveling Secret Feature


trick
Whats about to happen next is so mind-bendingly awesome that you
should probably get a drink to prepare yourself. Its like combining a T-800
from The Terminator, with the dreams-within-dreams layering from Inception, with the momentary inversion of reality that occurs when the quantum field surrounding an inductive proof collapses and everything snaps
into place.
Its. Really. Cool.
Got your drink? Okay, lets do this.
Lets go back to our original, recursive binomial function:
def binomial(n, k):
if k == 0:
return 1
return n * binomial(n - 1, k - 1) // k
Our goal is to create an equivalent iterative version that exactly preserves
the properties of the original. Well, how the hell are we going to do that?
Hold that thought for a sec.
Lets just stop for a moment and think about what that recursive function is
doing. We call it to compute some answer xt . But that answer depends on

some earlier answer xt1 that the function doesnt know, so it calls itself to
get that answer. And so on. Until, finally, it finds one concrete answer x0

that it actually knows and, from which, it can build every subsequent answer.
So, in truth, our answer xt is just the final element in a whole timeline of
needed earlier answers:

x0 , x1 , xt .
Well, so what? Why should we care?
Because weve watched The Terminator about six hundred times! We know
how to get rid of a problem in the present when weve seen its timeline: We
send a Terminator into the past to rewrite that timeline so the problem never gets
created in the rst place.
And our little recursive problem with binomial here? Weve seen its timeline.
Thats right. Its about to get real.
One more thing. Every single one of these steps preserves the original functions
behavior. You can run your unit tests after every step, and they should all
pass.
Lets begin.
1. Send a T-800 terminator unit into the functions timeline, back to
the time of x0 .

Oh, we dont have a T-800 terminator unit? No problem. Assume weve


sent a T-800 terminator unit into the functions timeline, back to the
time of x0 .

That was easy. Whats next?


2. Move the recursive call and surrounding work into a helper function.

This might seem like overkill but prevents mistakes. Do you make mistakes? Then just make the helper function already. Im calling it step
for reasons that will shortly become obvious.
def step(n, k):
return n * binomial(n - 1, k - 1) // k
def binomial(n, k):
if k == 0:
return 1
return step(n, k)
3. Partition the helper function into its 3 fundamental parts.
They are:

1. the recursive call to get the previous answer xi1 ,

2. the work to compute the current answer xi from xi1 , and


3. a return statement applied to xi alone:
def step(n, k):
previous_x = binomial(n - 1, k - 1)
x = n * previous_x // k
return x

# part (1)
# part (2)
# part (3)

def binomial(n, k):


if k == 0:
return 1
return step(n, k)
4. Secretly prepare to receive communications from the T-800 terminator unit.
You see, once the T-800 gets its cybernetic hands on earlier values in
the timeline, we can use its knowledge to eliminate the recursion. We
just need some way to get that knowledge from the T-800, now stuck in
the past, to us, stuck in the present.
Fortunately, weve seen time-travel movies, so we know exactly how
this problem is solved. Well just use a dead drop! Thats a prearranged

secret location where the T-800 will drop values in the past and where
we will check for them in the future.
So, per prior arrangement with the T-800, well extend our helper
function with a secret feature that checks the dead drop for a previous
value xi1 and, if ones there, uses it to muahahahaha! break the recursive call chain:
def step(n, k, previous_x=None):
if previous_x is None:
previous_x = binomial(n - 1, k - 1)
x = n * previous_x // k
return x

# <- new stuff


# <
# <

def binomial(n, k):


if k == 0:
return 1
return step(n, k)
Ordinarily, we would think of the helper as a function of ni and ki that

computes xi . Thats because the xi1 argument can safely be ignored. It


has no effect unless the T-800 drops a value into it.
But if the T-800 does drop a value into it, we can see the function as
representing the transformation

(ni , ki , xi1 ) xi
Note that xi1 goes in and xi comes out. And from that little kernel of
power we can do some seriously crazy stuff. For example: reverse the
backward flow of time. That is, compute forward through the recursive
timeline instead of backward.
Yeah.
Read on.
5. Modify the helpers return statement to also pass through its non-

secret arguments.
def step(n, k, previous_x=None):
if previous_x is None:
previous_x = binomial(n - 1, k - 1)
x = n * previous_x // k
return (n, k, x) # <-- here
def binomial(n, k):
if k == 0:
return 1
return step(n, k)[2]

# <-- note also

Now our helper represents the transformation

(ni , ki , xi1 ) (ni , ki , xi )


Interesting.
6. Apply, to the non-secret arguments in the helpers return statement, the opposite of the transformations applied to them in the recursive call.
Since we passed n

1, k 1 into the recursive call, well pass

n + 1, k + 1 out through the return statement:

def step(n, k, previous_x=None):


if previous_x is None:
previous_x = binomial(n - 1, k - 1) # <-- look here
x = n * previous_x // k
return (n + 1, k + 1, x) # <-- apply opposite here
def binomial(n, k):
if k == 0:
return 1
return step(n, k)[2]
Now our helper represents the transformation

(ni , ki , xi1 ) (ni+1 , ki+1 , xi )

And now we can reverse the flow of time.


When we started out with our original recursive function, if we asked it

for xt , the function had to go back in the timeline to get xt1 . And to get

xt1 , it had to go back even further to get xt2 . And so on, chewing up
stack every backward step of the way to x0 . It was heartbreaking,
watching it work like that.
But now, we can step the other way. If we have any (ni , ki , xi1 ) we can

get xi straight away, no recursion required. In goes xi1 , out comes xi .


We can go forward.
Why, if we knew n1 , k1 , and x0 , we could compute the entire series

x0 , x1 , xt

with zero recursion by starting at i

= 0 and working for-

ward.
So lets get those values!
7. Determine the initial conditions at the start of the timeline.
For this simple example, most of you can probably determine the initial
conditions by inspection. But Im going to go through the process anyway. You never know when you might need it. So:
Whats the start of the timeline? Its when the recursive binomial
function calls itself so many times that it finally hits one of its base
cases, which defines the first entry in the timeline, anchoring the
timeline at time i

= 0 . The base cases in this example are easy to find

since weve already split out the step logic; all thats left in binomial is
the base-case logic. Its easy to see that there is only one base case, and
its when k

= 0:

def step(n, k, previous_x=None):


if previous_x is None:
previous_x = binomial(n - 1, k - 1)
x = n * previous_x // k
return (n + 1, k + 1, x)

def binomial(n, k):


if k == 0:
# <-- base case
return 1
return step(n, k)[2]
From this base case we know that at the very start of the timeline we
must have i

= 0 , k = 0, and x = 1 . Thus we know that k0 = 0 and


x0 = 1. And because the timeline ends at time i = t, and we know that
(nt , kt ) = (n, k), the original arguments binomial was called with.
Lets visualize what we know about the timeline so far:
time i :

ni :
ki :
xi :

0 1 2 t
n
0 k
1 ?

The answer we ultimately seek is xt , marked by a question mark in the


timeline.
Now to determine n1 and k1 . To do that, we must answer this question:
How do we go from (ni , ki ) to (ni+1 , ki+1 ) ?
We already know the answer! Our step helper computes

(ni , ki , xi1 ) (ni+1 , ki+1 , xi ). So look at its logic: How does it transform its n and k arguments? It adds one to them. Thus,
ni+1 = ni + 1,
ki+1 = ki + 1.
Therefore,

k1 = k0 + 1 = 0 + 1 = 1.
Since ki is i steps from k0

= 0 and each step adds +1 we have

ki = k0 + i(+1) = i.
And when i

= t we know from the equation above that t = kt = k .

Finally, we can compute n1 , which is t


the only ni that we know so far:

1 reverse steps from nt = n ,

n1 = nt (t 1)(+1) = n k + 1.
And now we have our initial conditions:

(n1 , k1 , x0 ) = (n k + 1, 1, 0).
So now our knowledge of the timeline looks like this:
time i :

ni :
ki :
xi :

0
1
2 t=k
nk nk+1
n
0
1

k
1

And with this knowledge, we can step forward through the timeline,
from time i

= 1 to time i = 2 , and so on, until finally, at the last step


when i = t we will have determined xt , our desired answer!
Lets do it!
8. In the main function, iterate the step helper t times, starting from
the initial conditions. Then return xt .

def step(n, k, previous_x=None):


if previous_x is None:
previous_x = binomial(n - 1, k - 1)
x = n * previous_x // k
return (n + 1, k + 1, x)
def binomial(n, k):
if k == 0:
return 1
t = k

# <- new

(n, k, previous_x) = (n - k + 1, 1, 1)
for _i in xrange(1, t + 1):
(n, k, previous_x) = step(n, k, previous_x)
return previous_x # = x_t

#
#
#
#

Boom! Thats 100% iterative. But theres more!


9. Remove the base cases from the original function.
Since our iterations start from a base case, the base cases are already
incorporated into our new, iterative logic.
def step(n, k, previous_x=None):
if previous_x is None:
previous_x = binomial(n - 1, k - 1)
x = n * previous_x // k
return (n + 1, k + 1, x)
def binomial(n, k):
# if k == 0:
# <- remove these lines
#
return 1 #
t = k
(n, k, previous_x) = (n - k + 1, 1, 1)
for _i in xrange(1, t + 1):
(n, k, previous_x) = step(n, k, previous_x)
return previous_x # = x_t
Boom again!
10. Remove the secret feature from the step function.
Since previous_x always gets a value now, we can make it a required
part of the function.
def step(n, k, previous_x): # <- remove default value for previous_x
# if previous_x is None:
# <- remove these 2 lines
#
previous_x = binomial(n - 1, k - 1) #
x = n * previous_x // k
return (n + 1, k + 1, x)
def binomial(n, k):
t = k
(n, k, previous_x) = (n - k + 1, 1, 1)

for _i in xrange(1, t + 1):


(n, k, previous_x) = step(n, k, previous_x)
return previous_x # = x_t
11. Inline the step function.
Were on it!
def binomial(n, k):
t = k
(n, k, previous_x) = (n - k + 1, 1, 1)
for _i in xrange(1, t + 1):
x = n * previous_x // k
(n, k, previous_x) = (n + 1, k + 1, x)
return previous_x # = x_t
12. Apply finishing touches.
This example is pretty tight already, but we can substitute away the x
variable.
And, because ki

= i , we can replace the

for loops induction variable

_i with ki and correspondingly eliminate ki from the step logic.

And, while were making stuff better, theres an obvious optimization.


One property of binomial coefficients is that ( k )

n
= ( nk
) . One property of our code is that it runs for t = k steps. So when k > n k , we
n

can reduce the number of steps by solving for ( nk ) instead. Lets add
n

a couple of lines at the start of the logic to claim this optimization.


And, of course, lets add a docstring were nice like that.
The final result:

def binomial(n, k):


"""Compute binomial coefficient C(n, k) = n! / (k! * (n-k)!)."""
if k > n - k:
k = n - k
# 'pute C(n, n-k) instead if it's easier
t = k
(n, previous_x) = (n - k + 1, 1)
for k in xrange(1, t + 1):

(n, previous_x) = (n + 1, n * previous_x // k)


return previous_x # = x_t
Lets review what we just did. Its mind blowing:
1. We sent an imaginary T-800 terminator unit back into the functions
timeline.
2. Then we added a secret feature to our function that allowed the T-800
to send us values from the timelines past.
3. Then we used those values to break the recursive chain, reverse the
backward flow of time, and compute the timeline forward and iteratively instead of backward and recursively.
4. The function then being fully iterative, we removed the secret feature,
and the imaginary T-800 winked out of existence. (In an ironic twist of
fate, the T-800, via the secret-feature dead drop, had delivered into
our hands precisely the knowledge we needed to write both of them out
of history!)
5. And now we have a fast, efficient, and property-preserving iterative
version of our original recursive function, and its about as good as
anything we could hope to conjure up from scratch. (See it in action
just one stack frame and easy on the ints, too.)
6. And most importantly we did it all using a mechanical, repeatable
process!

Thanks!
Well, thats it for this installment. I hope you enjoyed reading it as much as
I did writing it. If you liked it (or didnt), or if you found a mistake, or especially if you can think of any way to help make my explanations better, let
me know. Just post a comment or fire me a tweet at @tmoertel.
Until next time, keep your brain recursive and your Python code iterative.

1. In the visualization, you cant actually see the largest integer produced
by the recursive versions computation. Its produced between steps 32
and 33, when the return value from step 32 is multiplied by step 33s

n = 39 to arrive at an integer of log2 (39 48903492) 30. 8 bits,


which is then immediately divided by k = 9 to produce the final answer.

Site proudly generated by Hakyll

Tom Moertels Blog

Home About Archive

Tricks of the trade:


Recursion to Iteration, Part 3: Recursive
Data Structures
By Tom Moertel
Posted on June 3, 2013
Tags: programming, recursion, iteration, python, recursion-to-iteration series, tail calls, data
structures

This is the third article in a series on converting recursive algorithms into


iterative algorithms. If any of what follows seems confusing, you may want
to read the earlier articles first.
This is an extra article that I hadnt planned. Im writing it because in a
comment on the previous article a reader asked me to show a less mathematical example and suggested tree traversal. So thats the subject of this
article: Well take a binary tree and flatten it into a list, first recursively,
then iteratively.

The challenge
First, lets define a binary tree to be either empty or given by a node having
three parts: (1) a value, (2) a left subtree, and (3) a right subtree, where both
of the subtrees are themselves binary trees. In Haskell, we might define it

like so:
data BinaryTree a = Empty | Node a (BinaryTree a) (BinaryTree a)
In Python, which well use for the rest of this article, well say that None
represents an empty tree and that the following class represents a node:
import collections
Node = collections.namedtuple('Node', 'val left right')
# some sample trees having various node counts
tree0 = None # empty tree
tree1 = Node(5, None, None)
tree2 = Node(7, tree1, None)
tree3 = Node(7, tree1, Node(9, None, None))
tree4 = Node(2, None, tree3)
tree5 = Node(2, Node(1, None, None), tree3)
Let us now define a function to flatten a tree using an in-order traversal.
The recursive definition is absurdly simple, the data type having only two
cases to consider:
def flatten(bst):
# empty case
if bst is None:
return []
# node case
return flatten(bst.left) + [bst.val] + flatten(bst.right)
A few tests to check that it does what we expect:
def check_flattener(f):
assert f(tree0) == []
assert f(tree1) == [5]
assert f(tree2) == [5,
assert f(tree3) == [5,
assert f(tree4) == [2,
assert f(tree5) == [1,
print 'ok'
check_flattener(flatten)

7]
7, 9]
5, 7, 9]
2, 5, 7, 9]
# ok

Our challenge for today is to convert flatten into an iterative version. Oth-

er than a new trick partial evaluation the transformation is straightforward, so Ill move quickly.
Lets do this!

Eliminating the first recursive call


First, lets separate the base case from the incremental work:
def step(bst):
return flatten(bst.left) + [bst.val] + flatten(bst.right)
def flatten(bst):
if bst is None:
return []
return step(bst)
And lets break the incremental work into smaller pieces to see whats going on.
def step(bst):
left = flatten(bst.left)
left.append(bst.val)
right = flatten(bst.right)
left.extend(right)
return left
def flatten(bst):
if bst is None:
return []
return step(bst)
Lets try to get rid of the first recursive call by assuming that somebody has
passed us its result via a secret argument left :
def step(bst, left=None):
if left is None:
left = flatten(bst.left)
left.append(bst.val)
right = flatten(bst.right)
left.extend(right)

return left
def flatten(bst):
if bst is None:
return []
return step(bst)
And now well make step return values that parallel its input arguments:
def step(bst, left=None):
if left is None:
left = flatten(bst.left)
left.append(bst.val)
right = flatten(bst.right)
left.extend(right)
return bst, left # <-- add bst
def flatten(bst):
if bst is None:
return []
return step(bst)[-1]

# <-- note [-1]

In the first recursive call, the transformation applied to bst is .left , so we


want to apply the opposite transformation to bst in the returned values.
And whats the opposite of descending to a nodes left subtree? Its ascending to the nodes parent. So we want something like this:
# this code does not work!
def step(bst, left=None):
if left is None:
left = flatten(bst.left)
left.append(bst.val)
right = flatten(bst.right)
left.extend(right)
return get_parent(bst), left

# <-- need get_parent

But were stuck. We cant define get_parent because our tree data structure
doesnt keep track of parents, only children.
New plan: Maybe we can assume that someone has passed us the nodes parent and go from there?

But this plan hits the same brick wall: If we add a new argument to accept
the parent, we must for parallelism add a new return value to emit the
transformed parent, which is the parent of the parent. But we cant compute the parent of the parent because, as before, we have no way of implementing get_parent .
So we do what mathematicians do when their assumptions hit a brick wall:
we strengthen our assumption! Now we assume that someone has passed
us all of the parents, right up to the trees root. And that assumption gives us
what we need:
def step(bst, parents, left=None):
if left is None:
left = flatten(bst.left)
left.append(bst.val)
right = flatten(bst.right)
left.extend(right)
return parents[-1], parents[:-1], left
Note that were using the Python stack convention for parents ; thus the
immediate parent of bst is given by the final element parents[-1] .
As a simplification, we can eliminate the bst argument by considering it
the final parent pushed onto the stack:
def step(parents, left=None):
bst = parents.pop() # <-- bst = top of parents stack
if left is None:
left = flatten(bst.left)
left.append(bst.val)
right = flatten(bst.right)
left.extend(right)
return parents, left
Now that step requires the parents stack as an argument, the base function must provide it:
def flatten(bst):
if bst is None:
return []

parents = [bst]
return step(parents)[-1]
But we still havent eliminated the first recursive call. To do that, well need
to pass the step function a value for its left argument, which will cause
the recursive call to be skipped.
But we only know what that value should be for one case, the base case,
when bst is None ; then left must be [] . To get to that case from the trees
root, where bst is definitely not None , we must iteratively replicate the
normal recursive calls on bst.left until we hit the leftmost leaf node. And
then, to compute the desired result, we must reverse the trip, iterating the
step function until we have returned to the trees root, where the parents
stack must be empty:
def flatten(bst):
# find initial conditions for secret-feature "left"
left = []
parents = []
while bst is not None:
parents.append(bst)
bst = bst.left
# iterate to compute the result
while parents:
parents, left = step(parents, left)
return left
And just like that, one of the recursive calls has been transformed into iteration. Were halfway to the finish line!

Eliminating the second recursive


call
But we still have to eliminate that final recursive call to flatten , now sequestered in step . Lets take a closer look at that function after we make its
left argument required since it always gets called with a value now:

def step(parents, left):


bst = parents.pop()
left.append(bst.val)
right = flatten(bst.right)
left.extend(right)
return parents, left
To get rid of the recursive call to flatten , were going to use a new trick:
partial evaluation. Basically, were going to replace the call to flatten with
the function body of flatten , after we rename all its variables to prevent
conflicts. So lets make a copy of flatten and suffix all its variables with 1 :
def flatten1(bst1):
left1 = []
parents1 = []
while bst1 is not None:
parents1.append(bst1)
bst1 = bst1.left
while parents1:
parents1, left1 = step(parents1, left1)
return left1
And then lets make its arguments and return values explicit:
(bst1, ) = ARGUMENTS
left1 = []
parents1 = []
while bst1 is not None:
parents1.append(bst1)
bst1 = bst1.left
while parents1:
parents1, left1 = step(parents1, left1)
RETURNS = (left1, )
And then well drop this expansion into step :
def step(parents, left):
bst = parents.pop()
left.append(bst.val)
# -- begin partial evaluation -(bst1, ) = (bst.right, )
left1 = []
parents1 = []

while bst1 is not None:


parents1.append(bst1)
bst1 = bst1.left
while parents1:
parents1, left1 = step(parents1, left1)
(right, ) = (left1, )
# -- end partial evaluation -left.extend(right)
return parents, left
Now we can eliminate code by fusion across the partial-evaluation boundary.
First up: left1 . We can now see that this variable accumulates values that,
in the end, get appended to left (via the return variable right ). But we can
just as well append those values to left directly, eliminating left1 within
the boundary and the call to left.extend(right) without:
def step(parents, left):
bst = parents.pop()
left.append(bst.val)
# -- begin partial evaluation -(bst1, ) = (bst.right, )
# left1 = [] # <-- eliminate and use left instead
parents1 = []
while bst1 is not None:
parents1.append(bst1)
bst1 = bst1.left
while parents1:
parents1, left = step(parents1, left)
# (right, ) = (left, ) # <-- eliminated
# -- end partial evaluation -# left.extend(right) # <-- eliminated
return parents, left
For this next fusion, were going to need to recall our base function to get
the necessary outside scope:
def step(parents, left):
bst = parents.pop()
left.append(bst.val)
# -- begin partial evaluation --

(bst1, ) = (bst.right, )
parents1 = []
while bst1 is not None:
parents1.append(bst1)
bst1 = bst1.left
while parents1:
parents1, left = step(parents1, left)
# -- end partial evaluation -return parents, left
def flatten(bst):
left = []
parents = []
while bst is not None:
parents.append(bst)
bst = bst.left
while parents:
parents, left = step(parents, left)
return left
When flatten calls step and the code within the partially evaluated region
executes, it builds up a stack of nodes parents1 and then calls step iteratively to pop values off of that stack and process them. When its finished,
control returns to step proper, which then returns to its caller, flatten ,
with the values ( parents , left ). But look at what flatten then does with
parents : it calls step iteratively to pop values off of that stack and process
them in exactly the same way.
So we can eliminate the while loop in step and the recursive call! by returning not parents but parents + parents1 , which will make the while
loop in flatten do the exact same work.
def step(parents, left):
bst = parents.pop()
left.append(bst.val)
# -- begin partial evaluation -(bst1, ) = (bst.right, )
parents1 = []
while bst1 is not None:
parents1.append(bst1)
bst1 = bst1.left

# while parents1:
# <-- eliminated
#
parents1, left = step(parents1, left) #
# -- end partial evaluation -return parents + parents1, left # parents -> parents + parents1
And then we can eliminate parents1 completely by taking the values we
would have appended to it and appending them directly to parents :
def step(parents, left):
bst = parents.pop()
left.append(bst.val)
# -- begin partial evaluation -(bst1, ) = (bst.right, )
# parents1 = [] # <-- eliminated
while bst1 is not None:
parents.append(bst1) # parents1 -> parents
bst1 = bst1.left
# -- end partial evaluation -return parents, left # parents + parents1 -> parents
And now, once we remove our partial-evaluation scaffolding, our step
function is looking simple again:
def step(parents, left):
bst = parents.pop()
left.append(bst.val)
bst1 = bst.right
while bst1 is not None:
parents.append(bst1)
bst1 = bst1.left
return parents, left
For the final leg of our journey simplification lets inline the step logic
back into the base function:
def flatten(bst):
left = []
parents = []
while bst is not None:
parents.append(bst)
bst = bst.left
while parents:
parents, left = parents, left

bst = parents.pop()
left.append(bst.val)
bst1 = bst.right
while bst1 is not None:
parents.append(bst1)
bst1 = bst1.left
parents, left = parents, left
return left
Lets eliminate the trivial argument-binding and return-value assignments:
def flatten(bst):
left = []
parents = []
while bst is not None:
parents.append(bst)
bst = bst.left
while parents:
# parents, left = parents, left
bst = parents.pop()
left.append(bst.val)
bst1 = bst.right
while bst1 is not None:
parents.append(bst1)
bst1 = bst1.left
# parents, left = parents, left
return left

# = no-op

# = no-op

And, finally, factor out the duplicated while loop into a local function:
def flatten(bst):
left = []
parents = []
def descend_left(bst):
while bst is not None:
parents.append(bst)
bst = bst.left
descend_left(bst)
while parents:
bst = parents.pop()
left.append(bst.val)
descend_left(bst.right)
return left

And thats it! We now have a tight, efficient, and iterative version of our
original function. Further, the code is close to idiomatic.
Thats it for this time. If you have any questions or comments, just hit me
at @tmoertel or use the comment form below.
Thanks for reading!

Site proudly generated by Hakyll

Tom Moertels Blog

Home About Archive

Tricks of the trade:


Recursion to Iteration, Part 4: The
Trampoline
By Tom Moertel
Posted on June 12, 2013
Tags: programming, recursion, iteration, python, recursion-to-iteration series, tail calls, data
structures, trampolines

This is the fourth article in a series on converting recursive algorithms into


iterative algorithms. If you havent read the earlier articles first, you may
want to do so before continuing.
In the first article of our series, we showed that if you can convert an algorithms recursive calls into tail calls, you can eliminate those tail calls to
create an iterative version of the algorithm using The Simple Method. In
this article, well look at another way to eliminate tail calls: the trampoline.
The idea behind the trampoline is this: before making a tail call, manually
remove the current execution frame from the stack, eliminating stack
build-up.

Execution frames and the stack

To understand why we might want to manually remove an execution frame,


lets think about what happens when we call a function. The language runtime needs some place to store housekeeping information and any local
variables the function may use, so it allocates a new execution frame on the
stack. Then it turns control over to the function. When the function is done,
it executes a return statement. This statement tells the runtime to remove
the execution frame from the stack and to give control (and any result) back
to the caller.
But what if the function doesnt return right away? What if it makes another function call instead? In that case, the runtime must create a new execution frame for that call and push it onto the stack, on top of the current
frame. If the function ends up calling itself many times recursively, each
call will add another frame to the stack, and pretty soon we will have eaten
up a lot of stack space.

Eliminating stack build-up


To avoid this problem, some programming languages guarantee that they
will recycle the current execution frame whenever a function makes a tail
call. That is, if the function calls some other function (or itself recursively)
and just returns that functions result verbatim, thats a tail call. In that
case, the runtime will recycle the current functions execution frame before
transferring control to the other function, making it so that the other function will return its result directly to the original functions caller. This
process is called tail-call elimination.
But in languages like Python that dont offer tail-call elimination, every
call, even if its a tail call, pushes a new frame onto the stack. So if we want
to prevent stack build-up, we must somehow eliminate the current frame
from the stack ourselves, before making a tail call.
But how? The only obvious way to eliminate the current frame is to return

to our caller. If were to make this work, then, the caller must be willing to
help us out. Thats where the trampoline comes in. Its our co-conspirator
in the plot to eliminate stack build-up.

The trampoline
Heres what the trampoline does:
1. It calls our function f , making itself the current caller.
2. When f wants to make a recursive tail call to itself, it returns the instruction call(f)(*args, **kwds) . The language runtime dutifully removes the current execution frame from the stack and returns control
to the trampoline, passing it the instruction.
3. The trampoline interprets the instruction and calls f back, giving it
the supplied arguments, and again making itself the caller.
4. This process repeats until f wants to return a final result z ; then it returns the new instruction result(z) instead. As before, the runtime removes the current execution frame from the stack and returns control
to the trampoline.
5. But now when the trampoline interprets the new instruction it will return z to its caller, ending the trampoline dance.
Now you can see how the trampoline got its name. When our function uses
a return statement to remove its own execution frame from the stack, the
trampoline bounces control back to it with new arguments.
Heres a simple implementation. First, we will encode our instructions to
the trampoline as triples. Well let call(f)(*args, **kwds) be the triple
(f, args, kwds) , and result(z) be the triple (None, z, None) :
def call(f):
"""Instruct trampoline to call f with the args that follow."""
def g(*args, **kwds):
return f, args, kwds

return g
def result(value):
"""Instruct trampoline to stop iterating and return a value."""
return None, value, None
Now well create a decorator to wrap a function with a trampoline that will
interpret the instructions that the function returns:
import functools
def with_trampoline(f):
"""Wrap a trampoline around a function that expects a trampoline."""
@functools.wraps(f)
def g(*args, **kwds):
h = f
# the trampoline
while h is not None:
h, args, kwds = h(*args, **kwds)
return args
return g
Note that the trampoline boils down to three lines:
while h is not None:
h, args, kwds = h(*args, **kwds)
return args
Basically, the trampoline keeps calling whatever function is in h until that
function returns a result(z) instruction, at which time the loop exits and z
is returned. The original recursive tail calls have been boiled down to a
while loop. Recursion has become iteration.

Example: factorial
To see how we might use this implementation, lets return to the factorial
example from the first article in our series:
def factorial(n):
if n < 2:
return 1

return n * factorial(n - 1)
Step one, as before, is to tail-convert the lone recursive call:
def factorial(n, acc=1):
if n < 2:
return acc
return factorial(n - 1, acc * n)
Now we can create an equivalent function that uses trampoline idioms:
def trampoline_factorial(n, acc=1):
if n < 2:
return result(acc)
return call(trampoline_factorial)(n - 1, n * acc)
Note how the return statements have been transformed.
Finally, we can wrap this function with a trampoline to get a callable version that we can use just like the original:
factorial = with_trampoline(trampoline_factorial)
Lets take it for a spin:
>>> factorial(5)
120
To really see whats going on, be sure to use the Online Python Tutors visualizer to step through the original, tail-recursive, and trampoline versions
of the function. Just open this link: Visualize the execution. (ProTip: use a
new tab.)

Why use the trampoline?


As I mentioned at the beginning of this article, if you can convert a functions recursive calls into tail calls which you must do to use a trampoline
you can also use the Simple Method to convert the functions recursion
into iteration, eliminating the calls altogether. For example, heres what

the Simple Method does to our original factorial function:


def factorial(n, acc=1):
while n > 1:
(n, acc) = (n - 1, acc * n)
return acc
This version is simpler and more efficient than the trampoline version. So
why not use the Simple Method always?
The answer is that the Simple Method is tricky to apply to functions that
make tail calls from within loops. Recall that it introduces a loop around a
functions body and replaces recursive tail calls with continue statements.
But if the function already has its own loops, replacing a tail call within one
of them with a continue statement will restart that inner loop instead of
the whole-body loop, as desired. In that case, you must add condition flags
to make sure the right loop gets restarted, and that gets old fast. Then, using a trampoline may be a win.
That said, I almost never use trampolines. Getting a function into tail-call
form is nine tenths of the battle. If Ive gone that far already, Ill usually go
the rest of the way to get a tight, iterative version.
Why, then, did we make this effort to understand the trampoline? Two reasons. First, its semi-common in programming lore, so its best to know
about it. Second, its a stepping stone to a more-general, more-powerful
technique: continuation-passing-style expressions. Thats our subject for next
time.
In the meantime, if you want another take on trampolines in Python, Kyle
Miller wrote a nice article on the subject: Tail call recursion in Python.
Thanks for reading! As always, if you have questions or comments, please
leave a comment on the blog or hit me at @tmoertel.

Site proudly generated by Hakyll

S-ar putea să vă placă și