Skip to content

Instantly share code, notes, and snippets.

@jchaplin2
Forked from wchan2/.gitignore
Last active August 29, 2015 14:17
Show Gist options
  • Save jchaplin2/100ded13703d31c08a7c to your computer and use it in GitHub Desktop.
Save jchaplin2/100ded13703d31c08a7c to your computer and use it in GitHub Desktop.
npm-debug.log
node_modules

JavaScript Functional Programming Exercise

An exercise to challenge the understanding of Functional Programming in JavaScript.

Requirements

  1. node.js installed
  2. node package manager (npm) installed -- should be installed with the latest version of node

Installing the Requirements

Run the below command in the terminal after having npm installed to install dependencies specified in package.json.

npm install

Exercise Instructions

There are a series of functions defined in functional.js that does not have computational logic yet. The task is to write the body for the defined specifications in the comments above each definition.

  1. Write the code to pass the in functional.js.
  2. Run the tests to make sure the tests passes.
  3. Iterate steps 1 and 2 until all steps are passing.

Running the Tests

In the terminal execute the below commands.

Watch for any file changes and run the tests.

grunt watch

Run the tests

grunt test

Run the JSHint

JSHint checks for inconsistencies in code that are typically not great practice. This could potentially help with the refactor exercise.

grunt jshint
;(function(globals) {
'use strict';
// Write a function that takes a parameter n and returns the factorial of the value
// ie. factorial(5) should return 5 * 4 * 3 * 2 * 1
var factorial = function(n) {
};
// Define a function, `memoize` that takes a function and returns a memoized version of the function such that:
// 1. it returns true if the given arguments to the new function has been computed and is in the cache before
// 2. and false otherwise
var memoize = function(fn) {
};
// Curried function
// Write a function `average` that is curried to accept 3 parameters and computes the average of the values
var average = function() {
};
// Bonus:
// Write a function that has the an interface where you can initialize a value and
// reduce with respect to that value in a callback function
// ie. `inject(0).reduce([1, 2, 3], function(sum, value) { return sum + value; })` will return the sum of all the values in the array
var inject = function(injected) {
};
globals.factorial = factorial;
globals.memoize = memoize;
globals.average = average;
globals.inject = inject;
})(exports);
var exercise = require('./functional');
describe('factorial', function() {
'use strict';
var factorial;
beforeEach(function() {
factorial = exercise.factorial;
});
it('is a function', function() {
expect(factorial).toEqual(jasmine.any(Function));
});
it('computes the correct value', function() {
expect(factorial(5)).toEqual(120);
});
describe('given 0', function() {
it('should return 1', function() {
expect(factorial(0)).toEqual(1);
});
});
describe('given 1', function() {
it('should return 1', function() {
expect(factorial(1)).toEqual(1);
});
});
});
describe('memoize', function() {
'use strict';
var memoize;
beforeEach(function() {
memoize = exercise.memoize;
});
it('is a function', function() {
expect(memoize).toEqual(jasmine.any(Function));
});
it('returns a function', function() {
expect(memoize(function() {})).toEqual(jasmine.any(Function));
});
describe('the returned function', function() {
var returnedFunc;
beforeEach(function() {
returnedFunc = memoize(function(num1, num2) {
return num1 + num2;
});
});
describe('given parameters that it has not been seen', function() {
it('returns false', function() {
expect(returnedFunc(1, 2)).toEqual(false);
});
});
describe('given parameters that it has already seen', function() {
beforeEach(function() {
returnedFunc(1, 2);
});
it('returns true', function() {
expect(returnedFunc(1, 2)).toEqual(true);
});
});
});
});
// average tests are not exhaustive as the parameter values could be given in many different combinations
describe('average', function() {
'use strict';
var average;
beforeEach(function() {
average = exercise.average;
});
it('is a function', function() {
expect(average).toEqual(jasmine.any(Function));
});
describe('when taking 1 parameter', function() {
it('returns a function', function() {
expect(average(1)).toEqual(jasmine.any(Function));
});
});
describe('when taking 2 parameters', function() {
it('returns a function on the first call', function() {
expect(average(1, 2)).toEqual(jasmine.any(Function));
});
describe('in two separate calls', function() {
it('returns a function on the first and second call', function() {
expect(average(1)(2)).toEqual(jasmine.any(Function));
});
});
});
describe('when taking 3 parameters', function() {
it('returns the proper result', function() {
expect(average(3, 6, 9)).toEqual(6);
});
describe('in two separate calls', function() {
it('returns the proper result taking 2 parameters and then 1 parameter', function() {
expect(average(3, 6)(9)).toEqual(6);
});
it('returns the proper result taking 1 and 2 parameters', function() {
expect(average(9)(3, 6)).toEqual(6);
});
});
describe('in three separate calls', function() {
it('returns a the proper result', function() {
expect(average(9)(3)(6)).toEqual(6);
});
});
});
});
describe('inject', function() {
'use strict';
var inject;
beforeEach(function() {
inject = exercise.inject;
});
it('is a function', function() {
expect(inject).toEqual(jasmine.any(Function));
});
it('reduces to a proper value', function() {
var sum = inject(0).reduce([1, 2, 3, 6], function(sum, value) { return sum + value; });
expect(sum).toEqual(12);
});
it('reduces to a proper value', function() {
var product = inject(1).reduce([4, 3, 2, 6], function(product, value) {
return product * value;
});
expect(product).toEqual(144);
});
describe('the returned object', function() {
it('has a reduce method', function() {
expect(inject(0)).toEqual({ reduce: jasmine.any(Function) });
});
});
});
var sys = require('sys'),
exec = require('child_process').exec;
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
jshint: {
files: ['functional.js', 'functional_spec.js'],
options: {
globals: {
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files %>'],
tasks: ['jshint']
}
});
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('lint', ['jshint']);
grunt.registerTask('default', ['jshint']);
grunt.registerTask('test', 'Runs the jasmine tests inside the specs folder', function() {
var done = this.async();
exec('jasmine-node functional_spec.js', function(error, stdout, stderror) {
sys.print(stdout, stderror);
if (error) {
done(false);
} else {
done();
}
});
});
grunt.event.on('watch', function() {
exec('grunt test', function(error, stdout, stderror) {
sys.print(stdout, stderror);
});
});
};
{
"name": "functional.js",
"version": "0.0.1",
"description": "An functional programming exercise.",
"main": "functional.js",
"devDependencies": {
"grunt": "0.4.2",
"grunt-contrib-jasmine": "0.5.2",
"grunt-contrib-watch": "0.5.3",
"grunt-contrib-jshint": "0.8.0",
"grunt-template-jasmine-requirejs": "0.1.9",
"grunt-jasmine-node": "^0.2.1"
},
"scripts": {
"preinstall": "npm install -g grunt-cli"
},
"repository": "",
"author": "William Chan",
"license": "MIT"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment