Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
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