Created
November 13, 2020 14:19
-
-
Save circlecube/3ead7d1a46f57842a8b7276698eb3ca4 to your computer and use it in GitHub Desktop.
Fun with functions
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
/* | |
* Fun with functions | |
* | |
* Function exercieses from Douglas Crockford | |
* in his The Good Parts of JavaScript and the Web | |
* course on Frontendmasters | |
* https://frontendmasters.com/courses/good-parts-javascript-web/ | |
*/ | |
// function that takes two numbers and return their sum | |
// add(3, 4) // 7 | |
function add(first, second) { | |
return first + second; | |
} | |
// function that takes two numbers and return their difference | |
// sub(3, 4) // -1 | |
function sub(first, second) { | |
return first - second; | |
} | |
// function that takes two numbers and return their product | |
// mul(3, 4) // 12 | |
function mul(first, second){ | |
return first * second; | |
} | |
// function that takes and argument and returns a function that returns that argumanet. | |
// identityf(3) // 3 | |
function identityf(x) { | |
return function () { | |
return x; | |
}; | |
} | |
// function that adds from two invocations | |
// addf(3)(4) // 7 | |
function addf(first) { | |
return function (second) { | |
return first + second; | |
}; | |
} | |
// function that takes a function and makes it callable with two invocations. | |
// higher order function. | |
// receives a function as parameter and returns another function as result | |
// liftf(add)(3)(4) // 7 | |
// liftf(mul)(3)(4) // 12 | |
function liftf(binary) { | |
return function (first) { | |
return function (second) { | |
return binary(first, second); | |
}; | |
}; | |
} | |
// function that takes a binary function and an argument, | |
// and returns a function that can take a second argument | |
// curry(add, 3)(4); // 7 | |
// curry(mul, 5)(6); // 30 | |
function curry(binary, first) { | |
return function (second) { | |
return binary(first, second); | |
}; | |
} | |
function curry2(binary, first) { | |
return liftf(binary)(first); | |
} | |
// with es6 doesn't need to be a binary function anymore | |
function curryES6(func, ...first) { | |
return function(...second) { | |
return func(...first, ...second); | |
} | |
} | |
// function that creates an inc function to add a number to another | |
// inc(5); // 6 | |
// inc(inc(5)); // 7 | |
var inc1 = addf(1); | |
var inc2 = liftf(add)(1); | |
var inc3 = curry(add, 1); | |
// function that takes a binary function and returns a unary function that | |
// passes its argument to the binary function twice | |
// doubl = twice(add); doubl(11); // 22 | |
// square = twice(mul); square(10); // 100 | |
function twice(binary) { | |
return function (a) { | |
return binary(a, a); | |
}; | |
} | |
// function that reverses the arguments of a binary function | |
// reverse(sub)(3, 2) // -1 | |
function reverse(binary){ | |
return function(first, second){ | |
return binary(second, first); | |
}; | |
} | |
// again with es6 it doesn't need to be binary | |
function reverseES6(func){ | |
return function (...args) { | |
return func(...args.reverse()); | |
}; | |
} | |
// function takes two unary functions and returns a unary function that calls them both. | |
// composeu(doubl, square)(5); // 100 | |
function composeu(f, g) { | |
return function(a) { | |
return g(f(a)); | |
}; | |
} | |
// function that takes two binary functions and returns a function that calls them both. | |
// composeb(add, mul)(2, 3, 7); // 35 | |
function composeb(f, g) { | |
return function(a, b, c){ | |
return g( f(a, b), c ); | |
}; | |
} | |
// function that allows a binary function to be called a limited number of times. | |
// return undefined after limit expoires | |
function limit(binary, count){ | |
return function(a, b){ | |
if (count >= 1) { | |
count -= 1; | |
return binary(a, b); | |
} | |
return undefined; | |
}; | |
} | |
// function that produces a generator that will produce a series of values. | |
function from(x){ | |
return function(){ | |
return x += 1; | |
}; | |
} | |
function from(x){ | |
return function(){ | |
var next = start; | |
start += 1; | |
return next; | |
}; | |
} | |
// function that takes a generator and an end value, | |
// and returns a generator that will produce numbers up to that limit. | |
function to(gen, end) { | |
return function() { | |
var value = gen(); | |
if (value < end) { | |
return value; | |
} | |
return undefined; | |
}; | |
} | |
// function that produces a generator that will produce values in a range. | |
// var index = fromTo(0,3); | |
function fromTo(start, end){ | |
return to( | |
from(start), | |
end | |
); | |
} | |
// function that takes an array and a generator and returns a | |
// generator that will produce elements from the array | |
function element(array, gen) { | |
return function () { | |
var index = gen(); | |
if ( index !== undefined ) { | |
return array[index]; | |
} | |
}; | |
} | |
// function that takes and array and an optional generator that will produce elements from the array. | |
// If generator is not provided, produce each of the elements in the array. | |
function element2(array, gen) { | |
if (gen === undefined) { | |
gen = fromTo(0, array.length); | |
} | |
return function () { | |
var index = gen(); | |
if ( index !== undefined ) { | |
return array[index]; | |
} | |
}; | |
} | |
// function that takes a generator and an array and produces | |
// a function that will collect the results in the array. | |
function collect(gen, array) { | |
return function() { | |
var value = gen(); | |
if (value !== undefined) { | |
array.push(value); | |
} | |
return value; | |
}; | |
} | |
// function that takes a generator and a predicate and produces a generator | |
// that produces only the values approved by the predicate. | |
function filter(gen, predicate){ | |
return function() { | |
var value; | |
do { | |
value = gen(); | |
} while ( | |
value !== undefined && !predicate(value) | |
); | |
return value; | |
}; | |
} | |
function filterES6(gen, predicate){ | |
return function recur() { | |
var value = gen(); | |
if ( value === undefined || predicate(value) ) { | |
return value; | |
} | |
return recur(); | |
}; | |
} | |
// function that takes two generators and produces a generator that | |
// combines the sequences. | |
// var con = concat(fromTo(0,3), fronTo(0,2)); 0,1,2,0,1 | |
function concat(gen1, gen2){ | |
var gen = gen1; | |
return function(){ | |
var value = gen(); | |
if (value !== undefined) { | |
return value; | |
} | |
gen = gen2; | |
return gen(); | |
}; | |
} | |
// function that makes a function that generates unique symbols. | |
// generate symbol factory | |
// factory - function that does work | |
function gensymf(prefix){ | |
var number = 0; | |
return function() { | |
number += 1; | |
return prefix + number; | |
}; | |
} | |
// function that takes a unary function and a seed and returns a gensymf | |
// generate symbol factory factory | |
function gensymff(unary, seed){ | |
return function (prefix){ | |
var number = seed; | |
return function(){ | |
number = unary(number); | |
return prefix + number; | |
}; | |
}; | |
} | |
// function that returns a generator that will return th next fibonacci number. | |
function fibonaccif(a, b){ | |
var i = 0; | |
return function() { | |
var next; | |
switch (i) { | |
case 0: | |
i = 1; | |
return a; | |
case 1: | |
i = 2; | |
return b; | |
case default: | |
next = a + b; | |
a = b; | |
b = next; | |
return next; | |
} | |
}; | |
} | |
function fibonaccif2(a,b) { | |
return function(){ | |
var next = a; | |
a = b; | |
b += next; | |
return next; | |
} | |
} | |
function fibonacci3(a,b) { | |
return concat( | |
concat( | |
limit(identityf(a), 1), | |
limit(identityf(b), 1) | |
), | |
function fibonacci() { | |
var next = a + b; | |
a = b; | |
b = next; | |
return next; | |
} | |
); | |
} | |
// function that returns an object containing two functions | |
// that implement an up/down counter, hiding the counter | |
function counter(value){ | |
return { | |
up: function(){ | |
value+=1; | |
return value; | |
}, | |
down: function(){ | |
value -= 1; | |
return value; | |
} | |
}; | |
} | |
// function that takes a binary function and returns and object | |
// containing an invoke function that can invoke the binary function | |
// and a revoke function that disables the invoke function. | |
/* | |
var rev = revocable(add); | |
rev.invoke(3, 4); // 7 | |
rev.revoke(); | |
rev.invoke(4, 5); // undefined | |
*/ | |
function revocable(f){ | |
var active = true; | |
return { | |
invoke: function(a, b){ | |
if ( active ) { | |
return f(a, b); | |
} | |
return undefined; | |
}, | |
revoke: function(){ | |
active = false; | |
} | |
}; | |
} | |
function revocable2(binary){ | |
return { | |
invoke: function(first, second){ | |
if (binary !== undefined) { | |
return binary( first, second); | |
} | |
}, | |
revoke: function() { | |
binary = undefined; | |
} | |
} | |
} | |
// function that takes a value and an optional source string and | |
// returns them in an object. | |
function m(value, source) { | |
return { | |
value: value, | |
source: (typeof source === 'string') ? source : String(value) | |
}; | |
} | |
// function that takes two m objects and returns an m object | |
// adding the value and concatenating the source | |
function addm(a, b){ | |
return m( | |
a.value + b.value, | |
'(' + a.source + '+' + b.source + ')' | |
); | |
} | |
// function that takes a binary function and a string and | |
// returns a function that acts on m objects. | |
// (monad) | |
/* | |
var addm = liftm(add, '+'); | |
addm(m(3), m(4)) // {value:7, source: '(3+4)'} | |
*/ | |
function liftm(binary, op) { | |
return function (a, b){ | |
return m( | |
binary(a.value, b.value), | |
'(' + a.source + op + b.source + ')' | |
) | |
} | |
} | |
// function that takes a binary function and a string and | |
// returns a function that acts on m objects or strings. | |
function liftm(binary, op) { | |
if (typeof a === 'number') { | |
a = m(a); | |
} | |
if ( typeof b === 'number') { | |
b = m(b); | |
} | |
return function (a, b){ | |
return m( | |
binary(a.value, b.value), | |
'(' + a.source + op + b.source + ')' | |
) | |
} | |
} | |
// function that evaluates simple array expressions | |
// [mul, 5, 11] | |
function exp(value){ | |
return ( Array.isArray(value) ) ? | |
value[0]( | |
value[1], | |
value[2] | |
) : value; | |
} | |
// function that evaluates simple and nested array expressions. | |
function exp2(value){ | |
return ( Array.isArray(value) ) ? | |
value[0]( | |
exp2(value[1]), | |
exp2(value[2]) | |
) : value; | |
} | |
// function that adds from many invocations until it sees an empty invocation. | |
// retursion: a function that returns itself | |
function addg(first){ | |
function more(next) { | |
if (next === undefined){ | |
return first; | |
} | |
first += next; | |
return more; | |
} | |
if (first !=== undefined) { | |
return more; | |
} | |
} | |
// function that will take a binary function and apply it to many invocations | |
function liftg(binary){ | |
return function (first) { | |
if (first === undefined) { | |
return first; | |
} | |
return function more(next) { | |
if (next === undefined){ | |
return first; | |
} | |
first = binary(first, next); | |
return more; | |
}; | |
}; | |
} | |
// function that will build and array from many invocations. | |
function arrayg(first) { | |
var array = []; | |
function more(next) { | |
if ( next === undefined) { | |
return array; | |
} | |
array.push(next); | |
return more; | |
} | |
return more(first); | |
} | |
// function that takes a unary function, and | |
// returns a dunction that takes a callback and an argumant | |
function continuize(unary){ | |
return function(callback, value){ | |
return callback(unary(value)); | |
}; | |
} | |
function continuizeES6(any){ | |
return function(callback, ...x){ | |
return callback(any(...x)); | |
} | |
} | |
// array wrapper object with methods get, store and append, | |
// such that an attacker cannot get access to the private array. | |
function vector() { | |
var array = []; | |
return { | |
get: function get(i) { | |
return array[+i]; | |
}, | |
store: function store(i, v) { | |
array[+i] = v; | |
}, | |
append: function append(v) { | |
array[array.length] = v; | |
} | |
}; | |
} | |
// function that makes a publish/subscribe object. | |
// It will reliable deliver all publications to all subscribers in the right order. | |
function pubsub() { | |
var subscribers = []; | |
return Object.freeze({ | |
subscribe: function(subscriber) { | |
subscribers.push(subscriber); | |
}, | |
publish: function(publication){ | |
subscribers.forEach(function(s){ | |
setTimeout( function() { | |
s(publication); | |
}, 0); | |
} | |
} | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment