Share
Recursion, Iteration, & JavaScript: A love story
(presented at JS Heroes 2018: https://youtu.be/FmiQr4nfoPQ)
Recursion and iteration are two alternative paradigms for solving problems by breaking them down into smaller chunks; this talk is a deep dive into how these paradigms work, and how JS supports them both. There’s a fundamental tradeoff between the two: the stateful repetition of iteration is often more performant as it’s closer to the way our machines “think”, whereas many programmers find the self-referential abstraction of recursion easier to read & write. In JS prior to ES6, neither paradigm was especially easy to work with: iterative loops were laborious to read and write (not human-efficient), and recursion was limited by the fact that subsequent recursive calls to a function require additional frames added to the stack, potentially leading to stack overflow (not machine-efficient). Eventually, however, all three - recursion, iteration, and JS - were finally brought closer together. In this talk we’ll dive into the nature of the two paradigms, examine what an Iterable and a tail-recursive function are, and learn how Tail Call Optimization unites the two paradigms, allowing us to write code recursively and run it iteratively.
Anjana Sofia Vakil
Transcript
103 slides
-
location data platform for developers maps, search, navigation web, iOS,
Android, Unity... -
so m u t e I ♥ The Recurse Center
recurse.com outreachy.org -
Jon n B o “Pro m g Ae h s
e n ro k in n e t s” youtu.be/JjDsP5n2kSM - h/t Yo h -
You might optimize for speed… You might optimize for space...
But… there's a third thing you might want to optimize for, which is: years of your life required per program implementation. This is more important than those other things... or certainly equally important. Jon n B o “Pro m g Ae h s e n ro k in n e t s” youtu.be/JjDsP5n2kSM - h/t Yo h “ ” -
yu , I’m u n Shakespeare © 20th Century Fox
1996 - image via IMDb -
> var simpleTree = makeTree(simpleTreeNode); We l ♥t e function
simpleTreeNode(value) { return { value: value, left: undefined, right: undefined }; } function makeTree(nodeType) { var root = nodeType(0); root.left = nodeType(1); root.left.left = nodeType(2); root.right = nodeType(3); root.right.left = nodeType(4); root.right.right = nodeType(5); root.right.right.right = nodeType(6); return root; } 0 1 2 3 4 5 6 -
Recursive tree traversal function recursiveTraverse(tree) { // base case if
(!tree) return; // recursive case console.log(tree.value); recursiveTraverse(tree.left); recursiveTraverse(tree.right); } > recursiveTraverse(simpleTree); 0 1 2 3 4 5 6 -
Recursive tree traversal function recursiveTraverse(tree) { // base case if
(!tree) return; // recursive case console.log(tree.value); recursiveTraverse(tree.left); recursiveTraverse(tree.right); } > recursiveTraverse(simpleTree); 0 1 2 3 4 5 6 in iv e d & w i ! === hu -ef en -
Iterative tree traversal function iterativeTraverse(tree) { var todo = [tree];
while (todo.length) { var next = todo.shift(); if (next) { console.log(next.value); todo.unshift(next.left); todo.push(next.right); } } } > iterativeTraverse(simpleTree); 0 1 2 3 4 5 6 -
Iterative tree traversal function iterativeTraverse(tree) { var todo = [tree];
while (todo.length) { var next = todo.shift(); if (next) { console.log(next.value); todo.unshift(next.left); todo.push(next.right); } } } > iterativeTraverse(simpleTree); 0 1 2 3 4 5 6 ru j s e l, bu t i t i :/ -
JS Ite on ES6: • Iterables via [Symbol.iterator] • Iterators
via next() • Generators via function* and yield • for…of loops -
Iterables function iterableTreeNode(value) { let node = { value: value,
left: undefined, right: undefined }; function recurseyIterator() { } node[Symbol.iterator]= recurseyIterator; return node; } function recurTraverse(tree) { if (!tree) return; // base case console.log(tree.value); // recursive case recurTraverse(tree.left); recurTraverse(tree.right); } + Recursion -
Iterables function iterableTreeNode(value) { let node = { value: value,
left: undefined, right: undefined }; function recurseyIterator() { } node[Symbol.iterator]= recurseyIterator; return node; } function recurTrav(tree) { console.log(tree.value); // recursive case if (tree.left) recurTrav(tree.left); if (tree.right) recurTrav(tree.right); // base case (no children): just return } + Recursion -
Iterables function iterableTreeNode(value) { let node = { value: value,
left: undefined, right: undefined }; function recurseyIterator() { <output the value> // recursive case <if left, iterate over subtree> <if right, iterate over subtree> // base case: finish iteration } node[Symbol.iterator]= recurseyIterator; return node; } function recurTrav(tree) { console.log(tree.value); // recursive case if (tree.left) recurTrav(tree.left); if (tree.right) recurTrav(tree.right); // base case (no children): just return } + Recursion -
Iterables function iterableTreeNode(value) { let node = { value: value,
left: undefined, right: undefined }; function* recurseyIterator() { yield node.value; // recursive case if (node.left) yield* node.left; if (node.right) yield* node.right; // base case: finish iteration } node[Symbol.iterator]= recurseyIterator; return node; } function recurTrav(tree) { console.log(tree.value); // recursive case if (tree.left) recurTrav(tree.left); if (tree.right) recurTrav(tree.right); // base case (no children): just return } + Recursion -
Iterables function iterableTreeNode(value) { let node = { value: value,
left: undefined, right: undefined }; function* recurseyIterator() { yield node.value; // recursive case if (node.left) yield* node.left; if (node.right) yield* node.right; // base case: finish iteration } node[Symbol.iterator]= recurseyIterator; return node; } > let betterTree = makeTree(iterableTreeNode); > for (let val of betterTree) console.log(val); 0 1 2 3 4 5 6 -
JS It(er) ge b r: github.com/tc39/proposal-async-iteration tc39.github.io/ecma262 • Async Iterables
via [Symbol.asyncIterator] • AsyncIterators via next().then() • Async Generators • for await…of loops -
Recursion: the dream // recursiveInception.js 'use strict'; const recursiveInception =
(n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 a dream within a dream within a dream within a dream -
Recursion: the dream // recursiveInception.js 'use strict'; const recursiveInception =
(n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 a dream within a dream within a dream within a dream $ node recursiveInception.js 30 a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream 1 2 3 4 5 6 7 8 9 10 11 -
Recursion: the dream // recursiveInception.js 'use strict'; const recursiveInception =
(n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3000 a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a … you get the idea 1 2 3 4 5 6 7 8 9 10 11 -
Recursion: the dream // recursiveInception.js 'use strict'; const recursiveInception =
(n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 30000 1 2 3 4 5 6 7 8 9 10 11 -
Recursion: the reality // recursiveInception.js 'use strict'; const recursiveInception =
(n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 30000 /home/anjana/JSHeroes/recursiveInception.js:4 const recursiveInception = (n) => { ^ RangeError: Maximum call stack size exceeded at recursiveInception (recursiveInception.js:4:28) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) 1 2 3 4 5 6 7 8 9 10 11 -
Recursion: the reality // recursiveInception.js 'use strict'; const recursiveInception =
(n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 30000 /home/anjana/JSHeroes/recursiveInception.js:4 const recursiveInception = (n) => { ^ RangeError: Maximum call stack size exceeded at recursiveInception (recursiveInception.js:4:28) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) at recursiveInception (recursiveInception.js:6:16) 1 2 3 4 5 6 7 8 9 10 11 -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); 1 2 3 4 5 6 7 8 9 10 11 -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 1 2 3 4 5 6 7 8 9 10 11 -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 1 2 3 4 5 6 7 8 9 10 11 ^ s a k (li d e ) -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 1 2 3 4 5 6 7 8 9 10 11 f a > recursiveInception(3) let dreams = recursiveInception(2); return dreams + " within a dream"; -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(3) let dreams = recursiveInception(2); return dreams + " within a dream"; recursiveInception(2) let dreams = recursiveInception(1); return dreams + " within a dream"; )^ -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(3) let dreams = recursiveInception(2); return dreams + " within a dream"; recursiveInception(2) let dreams = recursiveInception(1); return dreams + " within a dream"; recursiveInception(1) let dreams = recursiveInception(0); return dreams + " within a dream"; )^ -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(3) let dreams = recursiveInception(2); return dreams + " within a dream"; recursiveInception(2) let dreams = recursiveInception(1); return dreams + " within a dream"; recursiveInception(1) let dreams = recursiveInception(1); return dreams + " within a dream"; recursiveInception(0) return "a dream"; )^ -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(3) let dreams = recursiveInception(2); return dreams + " within a dream"; recursiveInception(2) let dreams = recursiveInception(1); return dreams + " within a dream"; recursiveInception(1) let dreams = recursiveInception(1); return dreams + " within a dream"; recursiveInception(0) return "a dream"; ( ^ -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(3) let dreams = recursiveInception(2); return dreams + " within a dream"; recursiveInception(2) let dreams = recursiveInception(1); return dreams + " within a dream"; recursiveInception(1) let dreams = "a dream"; return dreams + " within a dream"; ( ^ -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(3) let dreams = recursiveInception(2); return dreams + " within a dream"; recursiveInception(2) let dreams = "a dream within a dream"; return dreams + " within a dream"; ( ^ -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(3) let dreams = "a dream within a dream within a dream" ; return dreams + " within a dream"; )^ -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 3 a dream within a dream within a dream within a dream 1 2 3 4 5 6 7 8 9 10 11 -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 30000 1 2 3 4 5 6 7 8 9 10 11 -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 30000 1 2 3 4 5 6 7 8 9 10 11 -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 30000 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(30000) let dreams=recursiveInception(29999); return dreams + " within a dream"; -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 30000 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(30000) let dreams=recursiveInception(29999); return dreams + " within a dream"; recursiveInception(29999) let dreams=recursiveInception(29998); return dreams + " within a dream"; )^ -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 30000 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(30000) let dreams=recursiveInception(29999); return dreams + " within a dream"; recursiveInception(29999) let dreams=recursiveInception(29998); return dreams + " within a dream"; recursiveInception(29998) let dreams=recursiveInception(29997); return dreams + " within a dream"; )^ -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 30000 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(30000) let dreams=recursiveInception(29999); return dreams + " within a dream"; recursiveInception(29999) let dreams=recursiveInception(29998); return dreams + " within a dream"; recursiveInception(29998) let dreams=recursiveInception(29997); return dreams + " within a dream"; )^ recursiveInception(29997) let dreams=recursiveInception(29996); return dreams + " within a dream"; -
Recursion ca s k // recursiveInception.js 'use strict'; const recursiveInception
= (n) => { if (n === 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } const depth = parseInt(process.argv[2]); console.log(recursiveInception(depth)); $ node recursiveInception.js 30000 RangeError: Maximum call stack size exceeded 1 2 3 4 5 6 7 8 9 10 11 recursiveInception(30000) let dreams=recursiveInception(29999); return dreams + " within a dream"; recursiveInception(29999) let dreams=recursiveInception(29998); return dreams + " within a dream"; recursiveInception(29998) let dreams=recursiveInception(29997); return dreams + " within a dream"; recursiveInception(29997) let dreams=recursiveInception(29996); return dreams + " within a dream"; :( -
wo n’t o to r e wi t a n
t f a s? -
JS Ta l Op i z i • Implementation feature
of (some*) JS engines • Lets us ♻ stack frames: don’t need to “remember” anything from calling context • Requires call in tail position: “proper tail calls” in ES6 -
Tail Recursion const recursiveInception = (n) => { if (n
=== 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } f o s e w ^ af r u s al Not -re s e -
Tail Recursion const recursiveInception = (n) => { if (n
=== 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } f o s e w ^ af r u s al Not -re s e const tailRecursiveInception = (n) => { } Ta l-re s e -
Tail Recursion const recursiveInception = (n) => { if (n
=== 0) return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } f o s e w ^ af r u s al Not -re s e const tailRecursiveInception = (n) => { const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } Ta l-re s e us e p n ^ to h e r o -
const recursiveInception = (n) => { if (n === 0)
return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } f o s e w ^ af r u s al Not -re s e const tailRecursiveInception = (n) => { const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } Ta l-re s e us e p n ^ to h e r o ^ ca t m a f Tail Recursion -
const recursiveInception = (n) => { if (n === 0)
return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } f o s e w ^ af r u s al Not -re s e const tailRecursiveInception = (n) => { const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } Ta l-re s e us um or ^ to “re b ” hi r Tail Recursion -
const recursiveInception = (n) => { if (n === 0)
return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } f o s e w ^ af r u s al Not -re s e const tailRecursiveInception = (n) => { const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } Ta l-re s e us um or ^ to “re b ” hi r in a c . ^ is ca Tail Recursion -
const recursiveInception = (n) => { if (n === 0)
return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } f o s e w ^ af r u s al Not -re s e const tailRecursiveInception = (n) => { const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } Ta l-re s e ^ re s e l in “ta si ” Tail Recursion -
const recursiveInception = (n) => { if (n === 0)
return "a dream"; let dreams = recursiveInception(n-1); return dreams + " within a dream"; } f o s e w ^ af r u s al Not -re s e const tailRecursiveInception = (n) => { const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } Ta l-re s e ^ re s e l la t g f o s Tail Recursion -
Ta l Op i z i const tailRecursiveInception = (n)
=> { const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); -
Ta l Op i z i const tailRecursiveInception = (n)
=> { const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); -
Ta l Op i z i const tailRecursiveInception = (n)
=> { const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); )^ incept(3, "a dream") return incept(2, "a dream within a dream"); -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); incept(3, "a dream") return incept(2, "a dream within a dream"); Ta l Op i z i incept(2, "a dream within a dream") return incept(1, "a… w… within a dream"); )^ -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); incept(3, "a dream") return incept(2, "a dream within a dream"); Ta l Op i z i incept(2, "a dream within a dream") return incept(1, "a… w… within a dream"); ♻ -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); incept(2, "a dream within a dream") return incept(1, "a… w… within a dream"); Ta l Op i z i -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); incept(2, "a dream within a dream") return incept(1, "a… w… within a dream"); Ta l Op i z i )^ incept(1, "a dream within a dream within a dream") return incept(0, "a… w… w… within a dream"); -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); incept(2, "a dream within a dream") return incept(1, "a… w… within a dream"); Ta l Op i z i incept(1, "a dream within a dream within a dream") return incept(0, "a… w… w… within a dream"); ♻ -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); incept(1, "a dream within a dream within a dream") return incept(0, "a… w… w… within a dream"); Ta l Op i z i -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); incept(1, "a dream within a dream within a dream") return incept(0, "a… w… w… within a dream"); Ta l Op i z i incept(0, "a dream within a dream within a dream within a dream") return "a dream within a dream within a dream within a dream"; )^ -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); incept(1, "a dream within a dream within a dream") return incept(0, "a… w… w… within a dream"); Ta l Op i z i incept(0, "a dream within a dream within a dream within a dream") return "a dream within a dream within a dream within a dream"; ♻ -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); incept(0, "a dream within a dream within a dream within a dream") return "a dream within a dream within a dream within a dream"; Ta l Op i z i -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return incept(3, "a dream"); incept(0, "a dream within a dream within a dream within a dream") return "a dream within a dream within a dream within a dream"; ( ^ Ta l Op i z i -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); tailRecursiveInception(3) return "a dream within a dream within a dream within a dream"; )^ Ta l Op i z i -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } > tailRecursiveInception(3); a dream within a dream within a dream within a dream Ta l Op i z i -
const tailRecursiveInception = (n) => { const incept = (n,
dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } console.log(tailRecursiveInception(3)); let n = 3; let dreams = "a dream"; while (n > 0) { dreams += " within a dream"; n--; } console.log(dreams); Recursion + T O ≈ Iteration ^ w i t ke s ^ ru s li h -
T O L // tailRecursiveInception.js 'use strict'; const tailRecursiveInception =
(n) => { const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } const depth = parseInt(process.argv[2]); console.log(tailRecursiveInception(depth)); 1 2 3 4 5 6 7 8 9 10 11 12 13 -
// tailRecursiveInception.js 'use strict'; const tailRecursiveInception = (n) => {
const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } const depth = parseInt(process.argv[2]); console.log(tailRecursiveInception(depth)); 1 2 3 4 5 6 7 8 9 10 11 12 13 $ node tailRecursiveInception.js 30000 T O L -
// tailRecursiveInception.js 'use strict'; const tailRecursiveInception = (n) => {
const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } const depth = parseInt(process.argv[2]); console.log(tailRecursiveInception(depth)); 1 2 3 4 5 6 7 8 9 10 11 12 13 $ node tailRecursiveInception.js 30000 /home/anjana/JSHeroes/tailRecursiveInception.js:5 const incept = (n, dreams) => { ^ RangeError: Maximum call stack size exceeded at incept (tailRecursiveInception.js:5:18) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) T O L -
// tailRecursiveInception.js 'use strict'; const tailRecursiveInception = (n) => {
const incept = (n, dreams) => { if (n === 0) return dreams; return incept(n-1, dreams+" within a dream"); } return incept(n, "a dream"); } const depth = parseInt(process.argv[2]); console.log(tailRecursiveInception(depth)); 1 2 3 4 5 6 7 8 9 10 11 12 13 $ node tailRecursiveInception.js 30000 /home/anjana/JSHeroes/tailRecursiveInception.js:5 const incept = (n, dreams) => { ^ RangeError: Maximum call stack size exceeded at incept (tailRecursiveInception.js:5:18) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) at incept (tailRecursiveInception.js:7:12) li ! be y ! T O L -
Ta l Op i z i Feature of (some*) JS
engines * On a c e; mo do im m T O kangax.github.io/compat-table/es6/#test-proper_tail_calls_(tail_call_optimisation) • Webkit/Safari has it: webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/ • V8/Chromium/Node had it (w/ flag), removed it: bugs.chromium.org/p/v8/issues/detail?id=4698 -
Ta l Op i z i Feature of (some*) JS
engines * On a c e; mo do im m T O kangax.github.io/compat-table/es6/#test-proper_tail_calls_(tail_call_optimisation) • Webkit/Safari has it: webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit/ • V8/Chromium/Node had it (w/ flag), removed it: bugs.chromium.org/p/v8/issues/detail?id=4698 Hot d ed ur ; di s o s o n ! • TCO issues explained in V8 blog: v8project.blogspot.ro/2016/04/es6-es7-and-beyond.html • TC39 proposal for explicit tail call syntax (inactive): github.com/tc39/proposal-ptc-syntax • Ongoing discussion/proposal for WebAssembly: github.com/WebAssembly/tail-call -
$ nvm use 6 Now using node v6.12.3 $ node
--harmony_tailcalls tailRecursiveInception.js 30000 T O L -
$ nvm use 6 Now using node v6.12.3 $ node
--harmony_tailcalls tailRecursiveInception.js 30000 a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within a dream within ………………….. T O L -
Fur r a n : Exploring JS b D .
Axe R c ay exploringjs.com • ES6 - Iteration: Ch. 17, 21, 22; TCO: Ch. 27 • ES2018-ES2019: Asynchronous Iteration MDN developer.mozilla.org • JavaScript>Reference>Iteration_protocols • JavaScript>Guide>Functions>Recursion -
Mulțum ! JSHeroes organizers & Ioana Chiorean Mapbox The Recurse
Center community Max Rabkin @An a V ki ❤ an @ma x.co