CMSI 386

Homework #1

Partial Answers

Partial Answers

warmup.js

/* * A collection of functions illustrating some basic JavaScript. */ const request = require('request-promise'); const crypto = require('crypto'); /* * Returns an array with the minimum number of U.S. quarters, dimes, nickels, and * pennies, respectively, that make the given amount. This solution uses mutable * variables because it's rather common in JavaScript. If you want a more functional * solution see http://cs.lmu.edu/~ray/notes/functionalprogramming/. */ exports.change = (amount) => { if (amount < 0) { throw new RangeError('amount cannot be negative'); } const result = []; let remaining = amount; [25, 10, 5, 1].forEach((value) => { result.push(Math.floor(remaining / value)); remaining %= value; }); return result; }; /* * Returns a copy of the string with apostrophes and double quotes removed. */ exports.stripQuotes = s => s.replace(/['"]/g, ''); /* * Returns a random permutation of a string. This is a swap-based implementation of * the Fisher-Yates shuffle, which is awesome. Don't use the random technique! See * http://sroucheray.org/blog/2009/11/array-sort-should-not-be-used-to-shuffle-an-array/. * This implementation works by swapping elements of an array in place. We should do * performance testing to compare it with a version that destroys one array or string * as it pulls characters from it and adds them to a result. */ exports.scramble = (s) => { const characters = s.split(''); let j = characters.length; while (j) { const i = Math.floor(Math.random() * j--); // eslint-disable-line no-plusplus [characters[j], characters[i]] = [characters[i], characters[j]]; } return characters.join(''); }; /* * Produces successive powers of a base, up to the given limit, passing each to a * callback. This is one of the few fairly decent uses of the classic for-statement, * I think. */ exports.powers = (base, limit, callback) => { let power = 1; while (power <= limit) { callback(power); power *= base; } }; /* * A generator that generates successive powers of a base, up to the given limit. */ exports.powersGenerator = function* (base, limit) { // eslint-disable-line func-names for (let power = 1; power <= limit; power *= base) { yield power; } }; /* * The famous chainable function problem, implemented in a readable fashion. It is * possible to code golf this problem, producing a function s, implemented like this: * * s=a=>a?b=>b?s(a+' '+b):a:'' * * The golfed version is very cool but isn't quite accurate, because it uses falsiness, * not undefinedness, as determining whether or not an argument was passed. */ exports.say = (firstWord) => { const words = []; function sayMore(word) { if (word === undefined) { return words.join(' '); } words.push(word); return sayMore; } return sayMore(firstWord); }; /* * Returns the interleaving of an array with a bunch of values. The lengths of the * array and the number of values do not need to be the same. */ exports.interleave = (a, ...b) => { const firstLength = a.length; const secondLength = b.length; const max = Math.max(firstLength, secondLength); const result = []; for (let i = 0; i < max; i += 1) { if (i < firstLength) result.push(a[i]); if (i < secondLength) result.push(b[i]); } return result; }; /* * Creates a cylinder object in the "Crockford Classes" style. There are no units for * the radius and height. */ exports.cylinder = (specification) => { let { radius = 1, height = 1 } = specification; const capArea = () => Math.PI * radius * radius; const surfaceArea = () => (2 * capArea()) + (2 * Math.PI * radius * height); const stretch = (factor) => { height *= factor; }; const widen = (factor) => { radius *= factor; }; const volume = () => Math.PI * radius * radius * height; const toString = () => `Cylinder with radius ${radius} and height ${height}`; return Object.freeze({ volume, surfaceArea, get radius() { return radius; }, get height() { return height; }, stretch, widen, toString, }); }; /* * Returns an array of two functions, an encyptor and a decryptor, each using a * given key and a given encryption algorithm. The encryptor turns a UTF-8 encoded * string into a hex-string; the decryptor does the reverse. */ exports.makeCryptoFunctions = (key, algorithm) => [ (data) => { const cipher = crypto.createCipher(algorithm, key); return cipher.update(data, 'utf-8', 'hex') + cipher.final('hex'); }, (data) => { const cipher = crypto.createDecipher(algorithm, key); return cipher.update(data, 'hex', 'utf-8') + cipher.final('utf-8'); }, ]; /* * Returns a promise for a name of the form "surname, name" obtained from the Uinames * API, for the given region and gender. */ exports.randomName = ({ region, gender }) => request({ method: 'GET', uri: 'https://uinames.com/api/', json: true, headers: { 'User-Agent': 'Homework Assignment from LMU' }, qs: { region, gender, amount: 1 }, }).then(p => `${p.surname}, ${p.name}`);