Here's a good post on lazy iteration using both generators and continuations. Note that continuations are function based, and this can provide a very efficient, lazy approach to iteration. Lazy Iteration with Continuation
Last active
August 8, 2017 04:37
-
-
Save panesofglass/bb1c85c2434f7b3af17e6f94860b72f2 to your computer and use it in GitHub Desktop.
Iron Yard FizzBuzz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// type for fizzbuzz results | |
type FizzBuzzResult = | |
| FizzBuzz | |
| Fizz | |
| Buzz | |
| Number of int | |
let fizzbuzz n = | |
match n % 3, n % 5 with | |
| 0, 0 -> FizzBuzz | |
| 0, _ -> Fizz | |
| _, 0 -> Buzz | |
| s -> Number n | |
for i in 1..100 do printfn "%A" (fizzbuzz i) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* function syntax | |
function fizzbuzzFactory(fizz = 3, fizzText = 'fizz', buzz = 5, buzzText = 'buzz') { | |
return function (i) { | |
return i % fizz === 0 && i % buzz === 0 ? fizzText + buzzText | |
: i % fizz === 0 ? fizzText | |
: i % buzz === 0 ? buzzText | |
: i.toString() | |
} | |
} | |
*/ | |
/* const declaration */ | |
const fizzbuzzFactory = (fizz = 3, fizzText = 'fizz', buzz = 5, buzzText = 'buzz') => { | |
return (i) => { | |
return i % fizz === 0 && i % buzz === 0 ? fizzText + buzzText | |
: i % fizz === 0 ? fizzText | |
: i % buzz === 0 ? buzzText | |
: i.toString() | |
} | |
} | |
/* Class syntax */ | |
class FizzbuzzFactoryClass { | |
constructor(private fizz = 3, | |
private fizzText = 'fizz', | |
private buzz = 5, | |
private buzzText = 'buzz') { | |
} | |
exec(i) { | |
return i % this.fizz === 0 && i % this.buzz === 0 ? this.fizzText + this.buzzText | |
: i % this.fizz === 0 ? this.fizzText | |
: i % this.buzz === 0 ? this.buzzText | |
: i.toString() | |
} | |
} | |
const fbFactoryClass = | |
// (i) => new FizzbuzzFactoryClass().exec(i) | |
// is the same as | |
new FizzbuzzFactoryClass().exec | |
// test | |
const fizzbuzzTest = fizzbuzzFactory() | |
console.assert(fizzbuzzTest(3) === 'fizz') | |
console.assert(fizzbuzzTest(5) === 'buzz') | |
console.assert(fizzbuzzTest(15) == 'fizzbuzz') | |
console.assert(fizzbuzzTest(49) !== 'buzz') | |
// production | |
const fizzbuzzProd = fizzbuzzFactory(2, 'fuzz', 7, 'bizz') | |
/* | |
for (let i = 1; i <= 100; i++) { | |
console.log(fizzbuzzProd(i)) | |
} | |
*/ | |
const loopFactory = (start = 1, end = 100, increment = 1) => { | |
// using recursion | |
const loop = (acc, x) => { | |
if (x <= end) { | |
acc.push(x) | |
return loop(acc, x + increment) | |
} else { | |
return acc | |
} | |
} | |
return loop([], start) | |
/* using a loop and mutation | |
const result = [] | |
for (let i = start; i <= end; i += increment) { | |
result.push(i) | |
} | |
return result | |
*/ | |
} | |
console.log(loopFactory(1000000, 1010000, 1000)) | |
// Chaining functions | |
console.log(loopFactory().map(fizzbuzzProd).filter((x) => x === 'fuzz' || x === 'bizz')) | |
// Test that all % 3 && % 5 values produce a 'fizzbuzz' result | |
const fizzes = | |
loopFactory() | |
.filter((x) => x % 3 === 0 && x % 5 === 0) | |
.reduce((acc, x) => acc && fizzbuzzTest(x) === 'fizzbuzz', true) | |
console.assert(fizzes === true) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// simplest possible function: no-op | |
function nop() { | |
} | |
console.log(nop()) | |
// identity function: the simplest "true" function | |
function id(a) { | |
return a | |
} | |
console.log(id(1)) | |
console.log(id('hello')) | |
// identity function using ES6 lambda with a const declaration | |
const id2 = (a) => a | |
console.log(id2(1)) | |
console.log(id2('hello')) | |
// Testing | |
// Functions are terrific for testing, so long as you keep them "pure". | |
// What's pure? It means you simply return a value. If you do anything | |
// else, it's much harder to test the result since whatever you changed | |
// is both hidden from the caller and potentially also being changed | |
// by something else. This property is also called "referential integrity". | |
// If you know you will get the same output for the same input every time, | |
// your function is trivial to test. | |
const add = (x, y) => x + y | |
console.assert(add(1, 1) === 2) | |
console.assert(add(2, 2) === 4) | |
//console.assert(add(2, 3) === 4, 'expected add(2,3) === 4, but it wasn\'t') | |
console.assert(add(1, add(1, 1)) === 3) | |
// Iteration | |
// Functions may seem a bit useless if you can't change things | |
// from within them. However, I am not saying you can never change | |
// things; only that a function is difficult or impossible to accurately | |
// test if you do. Let's say we want to make things testable. What will | |
// we do if we get an array and need to do something with it? | |
const test = [1,2,3,4] | |
const out = [] | |
for (let i = 0; i < test.length; i++) { | |
out.push(test[i] + 1) | |
} | |
console.log(out) | |
// We changed each value by adding one and adding it to a new array. | |
// JavaScript has many built-in functions for handling this type of | |
// work without requiring us to modify values in a loop. | |
const out2 = test.map((x) => x + 1) | |
console.log(out2) | |
// In many fewer lines of code, we pushed the loop into a single | |
// call to Array.prototype.map. Notice what's inside. It's a function. | |
// This makes `map` a "higher-order function". Any function that takes | |
// a function as a parameter or returns a function is considered | |
// higher-order. We'll take a look at the latter in a moment. Let's | |
// refactor this to extract our heavy-duty logic. | |
const add1 = (x) => x + 1 | |
const out3 = test.map(add1) | |
console.log('out3', out3) | |
// You are seeing that correctly. We could have written: | |
const out4 = test.map((x) => add1(x)) | |
// or even | |
const out5 = test.map(function (x) { return add1(x) }) | |
// but why bother? | |
// Also notice how easy it would be to test our add1 function. | |
console.assert(add1(1) === 2) | |
// Our code is getting smaller and more readable, at least in my opinion. | |
// Array has several other useful functions: | |
const filtered = test.filter((x) => x < 3) | |
const reduced = test.reduce((acc, x) => acc + x) | |
// What do you think these two functions will do? | |
console.log('filtered', filtered) | |
console.log('reduced', reduced) | |
// Higher-order functions | |
function uncurriedAdd(a, b) { | |
return a + b | |
} | |
console.log(uncurriedAdd(1, 1)) | |
console.log(uncurriedAdd(2, 2)) | |
function curriedAdd(a) { | |
return function(b) { | |
return a + b | |
} | |
} | |
console.log(curriedAdd(1)(1)) | |
console.log(curriedAdd(2)(2)) | |
console.log(curriedAdd(1)) | |
const add1 = (b) => curriedAdd(1)(b) | |
console.log(add1(1)) | |
// TODO: discuss compose (others from JS Allonge?) | |
const compose = <A, B, C>(c: (b: B) => C, b: (a: A) => B) => (a: A) => c(b(a)) | |
//const compose = (c, b) => (a) => c(b(a)) | |
const addOne = (x) => x + 1 | |
const double = (x) => x * 2 | |
const asString = (x) => x.toString() | |
const addOneAndDouble = compose(double, addOne) | |
const addOneAndDoubleString = compose(asString, compose(double, addOne)) | |
console.log(addOneAndDouble(1) === 4) | |
console.log(addOneAndDoubleString(1) === '4') | |
const pipeline = (...fns) => | |
(value) => | |
fns.reduce((acc, fn) => fn(acc), value) | |
const addOneAndDouble2 = pipeline(addOne, double) | |
const addOneAndDoubleString2 = pipeline(addOne, double, asString) | |
console.log(addOneAndDouble2(1) === 4) | |
console.log(addOneAndDoubleString2(1) === '4') | |
// Why care about these funny words, curry and uncurry? | |
// These have many and very useful applications. | |
// What is a class definition? | |
class Sample { | |
a | |
constructor(a) { | |
this.a = a | |
} | |
equals(b) { | |
return this.a === b | |
} | |
} | |
const s = new Sample(1) | |
console.log(s.equals(1)) | |
// A similar, yet simpler implementation using a curried function: | |
function Sample2(a) { | |
return function equals(b) { | |
return a === b | |
} | |
} | |
console.log(Sample2(1)(1)) | |
// But, that's not a class! I can't create an instance! | |
// Do you really need to? In many cases, you don't need to do so. | |
// Simply closing over a value is sufficient: | |
const s2 = Sample2(1) | |
console.log(s2(0)) | |
console.log(s2(1)) | |
console.log(s2(2)) | |
// You can indeed reuse the instance you create here. | |
// However, you cannot retrieve values once passed in. | |
// Is it possible to use this strategy to return more | |
// than just a simple function? | |
function Sample3(a) { | |
function equals(b) { | |
return a === b | |
} | |
function compare(b) { | |
if (a < b) { | |
return 1 | |
} else if (a > b) { | |
return -1 | |
} else { | |
return 0 | |
} | |
} | |
return { | |
equals, | |
compare | |
} | |
} | |
const s3 = Sample3(1) | |
console.log(s3.equals(1)) | |
console.log(s3.compare(0)) | |
console.log(s3.compare(1)) | |
console.log(s3.compare(2)) | |
// We could take this to another level and allow | |
// the consumer to specify behavior! | |
function Sample3HO(a, compare) { | |
function _equals(b) { | |
return a === b | |
} | |
function _compare(b) { | |
return compare(a, b) | |
} | |
return { | |
equals: _equals, | |
compare: _compare | |
} | |
} | |
const s3ho = Sample3HO(1, function (a, b) { | |
// Reverse the comparison | |
if (a > b) { | |
return 1 | |
} else if (a < b) { | |
return -1 | |
} else { | |
return 0 | |
} | |
}) | |
console.log(s3ho.equals(1)) | |
console.log(s3ho.compare(0)) | |
console.log(s3ho.compare(1)) | |
console.log(s3ho.compare(2)) | |
// That looks an awful lot like a class! | |
// Well yes, but the functions are per-instance, | |
// not shared as they would be in a class. | |
// What does that mean? | |
// JavaScript uses prototypal inheritance. | |
// Class instances all share a common prototype: | |
function Sample4(a) { | |
this.a = a | |
} | |
Sample4.prototype.equals = function equals(b) { | |
return this.a === b | |
} | |
Sample4.prototype.compare = function compare(b) { | |
if (this.a < b) { | |
return 1 | |
} else if (this.a > b) { | |
return -1 | |
} else { | |
return 0 | |
} | |
} | |
const s4 = new Sample4(1) | |
console.log(s4.equals(1)) | |
console.log(s4.compare(0)) | |
console.log(s4.compare(1)) | |
console.log(s4.compare(2)) | |
console.log('properties in Sample3') | |
for (let prop in s3) { | |
if (s3.hasOwnProperty(prop)) { | |
console.log(prop) | |
} | |
} | |
console.log('properties in Sample4') | |
for (let prop in s4) { | |
if (s4.hasOwnProperty(prop)) { | |
console.log(prop) | |
} | |
} | |
console.log('properties in Sample4 prototype') | |
const s4proto = s4.__proto__ | |
for (let prop in s4) { | |
if (s4proto.hasOwnProperty(prop)) { | |
console.log(prop) | |
} | |
} | |
console.log('properties in Sample class') | |
for (let prop in s) { | |
if (s.hasOwnProperty(prop)) { | |
console.log(prop) | |
} | |
} | |
console.log('properties in Sample prototype') | |
// Can't access the __proto__ on a class instance | |
const sproto = Sample.prototype | |
for (let prop in s) { | |
if (sproto.hasOwnProperty(prop)) { | |
console.log(prop) | |
} | |
} | |
// We can therefore see how classes are constructed. | |
// What can we do with this, and why would we ever | |
// care to use this archaic form when the lovely class | |
// keyword now exists? | |
// What can we learn from the above? | |
// 1. We can return a function from another function. | |
// 2. We can provide functions in object literals. | |
// 3. We can pass a function as an argument to another function. | |
// 4. A function can serve as the constructor of a class. | |
// Factory functions | |
// Let's combine all of the above to create a factory function: | |
function createSample() { | |
function Sample(a) { | |
this.a = a | |
} | |
Sample.prototype.equals = function equals(b) { | |
return this.a === b | |
} | |
return Sample | |
} | |
const MySample = createSample() | |
const s5 = new MySample(1) | |
console.log(s5.equals(1)) | |
// That seems hardly worth the effort. Let's see what else we could do ... | |
function createWow(behaviors) { | |
function Wow(name) { | |
this.name = name | |
} | |
function isFunction(functionToCheck) { | |
const getType = {}; | |
return functionToCheck && getType.toString.call(functionToCheck) === '[object Function]' | |
} | |
for (let b in behaviors) { | |
if (isFunction(behaviors[b])) { | |
Wow.prototype[b] = behaviors[b] | |
} | |
} | |
return Wow | |
} | |
// What do you think this will do? | |
const Wow = createWow({ | |
speak: function(word) { return word }, | |
add: function (a, b) { return a + b } | |
}) | |
const wow = new Wow('mom') | |
console.log(wow.name) | |
console.log(wow.speak('howdy')) | |
console.log(wow.add(1, 1)) | |
// How is this any different than what we did above? | |
console.log('properties of Wow') | |
for (let prop in wow) { | |
if (wow.hasOwnProperty(prop)) { | |
console.log(prop) | |
} | |
} | |
console.log('properties of Wow.prototype') | |
for (let prop in wow) { | |
if (wow.__proto__.hasOwnProperty(prop)) { | |
console.log(prop) | |
} | |
} | |
// This doesn't seem too useful. Why not just define the class | |
// the way you want it or use `extends` for proper subclasses? | |
// Great point. But you may see an opportunity for flipping this: | |
const FunctionalMixin = (behavior) => | |
function (target) { | |
for (let property in behavior) { | |
if (!target.hasOwnProperty(property)) { | |
Object.defineProperty(target, property, { | |
value: behavior[property], | |
writable: true | |
}) | |
} | |
} | |
return target | |
} | |
// Now we can define a mixin that will add compare to our Sample class | |
// we defined above: | |
const Comparable = FunctionalMixin({ | |
compare(b) { | |
if (this.a > b) { | |
return -1 | |
} else if (this.a < b) { | |
return 1 | |
} else { | |
return 0 | |
} | |
} | |
}) | |
Comparable(Sample.prototype) | |
// Unfortunately, TypeScript won't compile this. | |
//s.compare(1) | |
console.log('Comparable Sample', (<any>s).compare(1)) | |
// We've added functionality to an existing class by modifying its | |
// prototype, and an existing value picked up the behavior automatically. | |
// Is this useful? Sometimes, though you have to be careful with it. | |
// There are other, even more general approaches to adding behaviors | |
// that you should investigate, but they are also more complex. | |
// I'll also add that I don't typically use this style in my | |
// day-to-day work anymore, though I used to do this a lot. | |
// I've shown only a handful of things you can do with functions. | |
// Topics I didn't cover include decorators, variadic functions, | |
// traits, and many other things that involve a bit more explanation. | |
// However, I encourage you to explore some of these topics, | |
// and in particular, check out Reginald Braithwaite's | |
// JavaScript Allonge, which covers these and many more topics. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment