Recursion: Difference between revisions

From JookWiki
(Add example)
(Dump current changes)
Line 1: Line 1:
Warning: This article is a work in progress! No refunds if it moves!
Warning: This article is a work in progress! No refunds if it moves!
TODO: This article is about tail calls


- introduction/overview
- introduction/overview
Line 7: Line 9:
- lua
- lua


==Example==
==Examples==
Most introductions to recursion I've seen use this Fibonacci gem:
Most introductions to recursion I've seen use this Fibonacci gem:


Line 20: Line 22:
  print(fib(41))
  print(fib(41))
  -- prints 165580141
  -- prints 165580141
This code is confusing to me and to my computer. This ends up causing severe speed penalities:
This code is confusing and slow. You'd never see anyone actually use this code, so as far as I'm concerned it's not a real example. If I didn't know better, I'd think this is a ploy to make recursion look bad.


*fib(38) takes 6 seconds on my computer
A better example is this factorial code:
*fib(39) takes 10 seconds on my computer
*fib(40) takes 17 seconds on my computer
*fib(41) takes 30 seconds on my computer


fib(41) effectively takes 13 seconds to add 102334155 and 63245986 together. Ridiculous.
  function fac(n)
 
   if n <= 0 then
The issue here is that each step re-calculates all previous steps. Twice even!
 
An equivalent version without recursion is:
 
  function fib(n)
   if n <= 1 then
     return 1
     return 1
  end
  a, b = 0, 1
  for i = n, 1, -1 do
    a, b = b, a + b
  end
  return a
end
print(fib(41))
-- prints 165580141
This runs instantly on my machine. fib(100000000) takes 6 seconds on my computer.
At this point the impression you would get is that recursion is slow and ill suited for these tasks. Not so fast!
We can modify the example to store the previous two steps in the 'a' and 'b' function arguments:
function fib(n, a, b)
  if n <= 1 then
    return b
   else
   else
     return fib(n - 1, b, a + b)
     return n * fac(n - 1)
   end
   end
  end
  end
   
   
  print(fib(41, 0, 1))
  print(fac(20))
  -- prints 165580141
  -- prints 2432902008176640000


This version is just as fast as the iterative solution, it's just written recursively.
This looks pretty good and shows off the nature of recursion: Calling a


- mention stack overflow
- mention stack overflow
Line 98: Line 72:
- recursion-based control flow
- recursion-based control flow


==Tail call elimination==
==Tail call elimination ==
- floating back down to reality
- floating back down to reality


Line 111: Line 85:
- structured programming, goto wars
- structured programming, goto wars


==Mainstream support==
==Mainstream support ==
- functional programming languages
- functional programming languages



Revision as of 06:32, 2 February 2022

Warning: This article is a work in progress! No refunds if it moves!

TODO: This article is about tail calls

- introduction/overview

- 'magic of recursion'?

- lua

Examples

Most introductions to recursion I've seen use this Fibonacci gem:

function fib(n)
  if n <= 1 then
    return n
  else
    return fib(n - 1) + fib(n - 2)
  end
end
 
print(fib(41))
-- prints 165580141

This code is confusing and slow. You'd never see anyone actually use this code, so as far as I'm concerned it's not a real example. If I didn't know better, I'd think this is a ploy to make recursion look bad.

A better example is this factorial code:

function fac(n)
  if n <= 0 then
    return 1
  else
    return n * fac(n - 1)
  end
end

print(fac(20))
-- prints 2432902008176640000

This looks pretty good and shows off the nature of recursion: Calling a

- mention stack overflow

- imagine an infinite stack

Loops

- recursion can implement loops!

- implementing a for loop

- implementing a while loop

- implementing a do while loop

- each loop iteration only shares global and function args

State machines

- implementing a stateful algorithm

- some kind of menu system

- the code makes sense to read

- this is mutual recursion

- very hard to do in a traditional structured language

Lambdas

- lambdas to actually replace looping constructs/switch statements

- most mainstream languages support lambas

- recursion-based control flow

Tail call elimination

- floating back down to reality

- we've been writing code as there's no stack

- tail call elimination

- NOT an optimization, how many optimizations decide which way you can program?

- hints deeper at function calls vs jumps

- structured programming, goto wars

Mainstream support

- functional programming languages

- lua

- clang mustcall

- webassembly