Fun with functions
/* | |
* 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