Recursion
Prerequisites
- Javascript arrow functions
- Factorial
Dialogue
Do you know recursion?
- Yes
Do you really know recursion?
- Yeah, I would say so
No, but do you really know recursion?
- Alright, I’ll bite
Show me the Factorial structure
- Here you go
// factorial
(n) => {
// base case
if (n === 0) {
return 1
}
// recursive case
return n * factorial(n - 1)
}Could you do it if the language did not support recursion?
- I don’t know… yet but I can try
What would be a good starting point?
- Eliminating the recursive call
How would you do that?
- By abstracting out the recursive part
// factorialStructure
(toBeDetermined) => {
return (n) => {
if (n === 0) {
return 1
}
return n * toBeDetermined(n - 1)
}
}- How can we infer
toBeDetermined?
Lets try to reverse engineer. Can you tell that this behaves like factorial0 for input n = 0?
- Of Course, because that’s the base case
Does that mean we have factorial0?
- Yes, as long as we never reach the recursive case
Assumption: Infinity
To keep the focus of this write up on Recursion, we will assume that Infinity is a function that never produces a value.
const infinity = () => {
return infinity()
}So can we call this factorial0?
((toBeDetermined) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * toBeDetermined(n - 1)
}
}
})(infinity)- After reducing it to the following,
// factorial0
(n) => {
if (n === 0) {
return 1
}
else {
return n * infinity(n - 1)
}
}- It matches the behavior of
factorial0, and hence we can call itfactorial0
Now that we have factorial0, can we write factorialLessThanOrEqualTo1 as the following?
// factorialLessThanOrEqualTo1?
(n) => {
if (n === 0) {
return 1
}
else {
return n * factorial0(n - 1)
}
}
// which reduces to
(n) => {
if (n === 0) {
return 1
}
else {
return n * ((o) => {
if (o === 0) {
return 1
}
else {
return o * infinity(o - 1)
}
})(n - 1)
}
}
Given that this follows the behavior of factorial for input n = 0 and n = 1, it can be called factorialLessThanOrEqualTo1
So, is the following function factorialLessThanOrEqualTo2?
// factorialLessThanOrEqualTo2?
(n) => {
if (n === 0) {
return 1
}
else {
return n * ((o) => {
if (o === 0) {
return 1
}
else {
return o * ((p) => {
if (p === 0) {
return 1
}
else {
return p * infinity(p - 1)
}
})(o - 1)
}
})(n - 1)
}
}- Yeah, but this seems a lot. There is a lot of repetition here.
Yes, I agree we can abstract out the repeating bit
What does this look like?
((makeFactorial) => {
return makeFactorial(infinity)
})(
(factorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * factorial(n - 1)
}
}
}
)- I don’t know yet, I’ll have to evaluate this
How would you do that?
- I would do a single substitution. That would give us
((factorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * factorial(n - 1)
}
}
})(infinity)Can we reduce it further?
- Yes we can. That would give us…
(n) => {
if (n === 0) {
return 1
}
else {
return n * infinity(n - 1)
}
}- That looks exactly like
factorial0
Correct, So can we write factorialLessThanOrEqualTo1 using makeFactorial as the following?
// factorialLessThanOrEqualTo1
((makeFactorial) => {
return makeFactorial(
makeFactorial(infinity)
)
})(
(factorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * factorial(n - 1)
}
}
}
)(please try reducing this with pen and paper if possible)
What about factorialLessThanOrEqualTo2?
- Here you go
// factorialLessThanOrEqualTo2
((makeFactorial) => {
return makeFactorial(
makeFactorial(
makeFactorial(infinity)
)
)
})(
(factorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * factorial(n - 1)
}
}
}
)So is recursion a bunch of pending operations stacked on top of each other?
-
True while executing
-
That does work but how far can we keep going? We wouldn’t want to manually compose
makeFactorialto getfactorialLessThanEqualToN
I agree, lets see if we can automate this composition to work for all n values
- Where would we start?
makeFactorial
Since it doesnt matter what we pass to makeFactorial for the base case i.e. factorial0, can we give it makeFactorial instead of infinity?
- Sure, that would give us
factorial0as
// factorial0
((makeFactorial) => {
return makeFactorial(makeFactorial)
})(
(factorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * factorial(n - 1)
}
}
}
)Can we use the name makeFactorial instead of factorial?
- Sure, that doesn’t change the meaning. So factorial0 would look like
// factorial0
((makeFactorial) => {
return makeFactorial(makeFactorial)
})(
(makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(n - 1)
}
}
}
)- But why would we want to do that?
Because some names are more equal than others
Can we use the else branch to generate a recursive case?
// ?
((makeFactorial) => {
return makeFactorial(makeFactorial)
})(
(makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(infinity)(n - 1)
}
}
}
)- Yes, this gives us
factorialLessThanOrEqualTo1becausemakeFactorial(infinity)reduces to a function that behaves likefactorial0as we saw above
Can we do this more than once?
- How would we do that?
How about feeding makeFactorial to itself?
// ?
((makeFactorial) => {
return makeFactorial(makeFactorial)
})(
(makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(makeFactorial)(n - 1)
}
}
}
)- This feels unnatural
Is it because up until now, we were feeding makeFactorial a function that behaves like factorial for smaller inputs?
- BINGO
Well, makeFactorial IS the creator of such functions
- Ooo, but does it actually work?
Lets try, whats the output for input n = 0?
// ?
((makeFactorial) => {
return makeFactorial(makeFactorial)
})(
(makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(makeFactorial)(n - 1)
}
}
}
)- We can unwind this to get a flat function before applying for the input n = 0. Lets try substituting the arguments step by step
((makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(makeFactorial)(n - 1)
}
}
})(
(makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(makeFactorial)(n - 1)
}
}
}
)Can we reduce it even further?
- Yes we can
// reducedStructure
(n) => {
if (n === 0) {
return 1
}
else {
return n *
((makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(makeFactorial)(n - 1)
}
}
})(
(makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(makeFactorial)(n - 1)
}
}
}
)(n - 1)
}
}Do you know the answer for n = 0 now?
- Yes, its clearly the base case i.e. 1
What about n = 1?
- We can work it out. For n = 1, it will return the else branch i.e.
1 *
((makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(makeFactorial)(n - 1)
}
}
})(
(makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(makeFactorial)(n - 1)
}
}
}
)(1 - 1)which will reduce to…
1 *
((n) => {
if (n === 0) {
return 1
}
else {
return n *
((makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(makeFactorial)(n - 1)
}
}
})(
(makeFactorial) => {
return (n) => {
if (n === 0) {
return 1
}
else {
return n * makeFactorial(makeFactorial)(n - 1)
}
}
}
)(n - 1)
}
})(0)Does this seem familiar?
- Yeah, its the same structure that we started with, applied to a smaller input
1 * reducedStructure(0)- Since we already established that
reducedStructure(0)evaluates to1, we can conclude thatreducedStructure(1)evaluates to1 * 1 => 1
Can you try evaluating the same for input n = 2? (Please try this on paper)
Did you actually do it?
- Yes, it seems that no new structures appear and it self replicates until it reaches the base case at which point recursion ends and we get the output
Did we cook recursion at home?
-
Yes, yes we did, YAYYYY
-
But can we do this for any recursive pattern instead of just factorial?
Yes, I recommend you check out The Little Schemer. Thank you Daniel P. Friedman and Matthias Felleisen
Is everything a function?
- Always has been