From b7d85f733c047965b082148da723068531f6b762 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Tue, 13 Feb 2024 20:31:27 +0100 Subject: [PATCH 01/15] [ADD] basic functions - identity - addb - subb - mulb [ADD] tests + advanced testing for type errors --- Solutions/Bltzz_solution.js | 61 ++++ test/Bltzz_tests.js | 700 ++++++++++++++++++++++++++++++++++++ 2 files changed, 761 insertions(+) create mode 100644 Solutions/Bltzz_solution.js create mode 100644 test/Bltzz_tests.js diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js new file mode 100644 index 0000000..1f09252 --- /dev/null +++ b/Solutions/Bltzz_solution.js @@ -0,0 +1,61 @@ +/* + * @author Bltzz + * +*/ + +function _check (x, y) { + if (typeof x !== 'number') + throw new TypeError(`${x} is not a number`); + if (typeof y !== 'number') + throw new TypeError(`${y} is not a number`); +} + +/** + * identity(x) ⇒ any + * @param {*} x - Any value + * @returns {*} - The same value + */ +function identity(x){ + return x +} + +/** + * addb(a, b) ⇒ number + * @param {*} x - Any value + * @param {*} y - Any value + * @returns {*} - x plus y + */ +function addb(a, b) { + _check(a, b) + return a + b +} + +/** + * subb(a, b) ⇒ number + * @param {*} x - Any value + * @param {*} y - Any value + * @returns {*} - x minus y + */ +function subb(a, b) { + _check(a, b) + return a - b +} + +/** + * mulb(a, b) ⇒ number + * @param {*} x - Any value + * @param {*} y - Any value + * @returns {*} - x times y + */ +function mulb(a, b) { + _check(a, b) + return a * b +} + + +module.exports = { + identity, + addb, + subb, + mulb +} diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js new file mode 100644 index 0000000..f4e6073 --- /dev/null +++ b/test/Bltzz_tests.js @@ -0,0 +1,700 @@ +const assert = require('chai').assert; +const expect = require('chai').expect; +const filename = 'Bltzz_solution'; +const sol = require('../Solutions/' + filename); +require('mocha-sinon'); + + +describe("JS_Fun_Practice", function () { + + describe("identity()", function () { + + it("takes an argument and returns that argument", function () { + assert.equal(sol.identity(3), 3); + }); + + }); + + + describe("addb(a,b)", function () { + + it("takes two numbers and returns their sum", function () { + assert.equal(sol.addb(3, 4), 7); + }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.addb(40, '2')).to.throw(TypeError); + expect(() => sol.addb(40, [])).to.throw(TypeError); + expect(() => sol.addb(40, {})).to.throw(TypeError); + expect(() => sol.addb('40', 2)).to.throw(TypeError); + expect(() => sol.addb([], 2)).to.throw(TypeError); + expect(() => sol.addb({}, 2)).to.throw(TypeError); + }); + + }); + + + describe("subb(a,b)", function () { + + it("takes two numbers and returns their difference", function () { + assert.equal(sol.subb(3, 4), -1); + }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.subb(40, '2')).to.throw(TypeError); + expect(() => sol.subb(40, [])).to.throw(TypeError); + expect(() => sol.subb(40, {})).to.throw(TypeError); + expect(() => sol.subb('40', 2)).to.throw(TypeError); + expect(() => sol.subb([], 2)).to.throw(TypeError); + expect(() => sol.subb({}, 2)).to.throw(TypeError); + }); + + }); + + + describe("mulb(a,b)", function () { + + it("takes two numbers and returns their product", function () { + assert.equal(sol.mulb(3, 4), 12); + }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.mulb(40, '2')).to.throw(TypeError); + expect(() => sol.mulb(40, [])).to.throw(TypeError); + expect(() => sol.mulb(40, {})).to.throw(TypeError); + expect(() => sol.mulb('40', 2)).to.throw(TypeError); + expect(() => sol.mulb([], 2)).to.throw(TypeError); + expect(() => sol.mulb({}, 2)).to.throw(TypeError); + expect(() => sol.mulb("sfs", "b")).to.throw(TypeError); + }); + + }); + + + describe("minb(a,b)", function () { + + it("takes two numbers and returns the smaller one", function () { + assert.equal(sol.minb(3, 4), 3); + }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.mulb(40, '2')).to.throw(TypeError); + expect(() => sol.mulb(40, [])).to.throw(TypeError); + expect(() => sol.mulb(40, {})).to.throw(TypeError); + expect(() => sol.mulb('40', 2)).to.throw(TypeError); + expect(() => sol.mulb([], 2)).to.throw(TypeError); + expect(() => sol.mulb({}, 2)).to.throw(TypeError); + expect(() => sol.mulb("sfs", "b")).to.throw(TypeError); + }); + + }); + + + describe("maxb(a,b)", function () { + it("takes two numbers and returns the larger one", function () { + assert.equal(sol.maxb(3, 4), 4); + }); + }); + describe("add(...nums)", function () { + it("is an add fuction that is generalized for any amount of arguments", function () { + assert.equal(sol.add(1, 2, 4), 7); + }); + }); + describe("sub(...nums)", function () { + it("is a sub fuction that is generalized for any amount of arguments", function () { + assert.equal(sol.sub(1, 2, 4), -5); + }); + }); + describe("mul(...nums)", function () { + it("is a mul fuction that is generalized for any amount of arguments", function () { + assert.equal(sol.mul(1, 2, 4), 8); + }); + }); + describe("min(...nums)", function () { + it("is a min fuction that is generalized for any amount of arguments", function () { + assert.equal(sol.min(1, 2, 4), 1); + }); + }); + describe("max(...nums)", function () { + it("is a max fuction that is generalized for any amount of arguments", function () { + assert.equal(sol.max(1, 2, 4), 4); + }); + }); + describe("addRecurse(...nums)", function () { + it("is an add fuction that is generalized but uses recursion", function () { + assert.equal(sol.addRecurse(1, 2, 4), 7); + }); + }); + describe("mulRecurse(...nums)", function () { + it("is a mul fuction that is generalized but uses recursion", function () { + assert.equal(sol.mulRecurse(1, 2, 4), 8); + }); + }); + // describe("minRecurse(...nums)", function () { + // it("is a min fuction that is generalized but uses recursion", function () { + // assert.equal(sol.minRecurse(1, 2, 4), 1); + // }); + // }); + // describe("maxRecurse(...nums)", function () { + // it("is a max fuction that is generalized but uses recursion", function () { + // assert.equal(sol.maxRecurse(1, 2, 4), 4); + // }); + // }); + // describe("not(func)", function () { + // it("takes a function and returns the negation of its result", function () { + // const isOdd = (x) => x % 2 === 1; + // const isEven = sol.not(isOdd); + // assert.equal(isEven(1), false); + // assert.equal(isEven(2), true); + // }); + // }); + // describe("acc(func,initial)", function () { + // it(`takes a function and an initial value and returns a function + // that runs the initial function on each argument, accumulating the result`, function () { + // let add = sol.acc(sol.addb, 0); + // assert.equal(add(1, 2, 4), 7); + + // let mul = sol.acc(sol.mulb, 1); + // assert.equal(mul(1, 2, 4), 8); + // }); + // }); + // describe("accPartial(func,start,end)", function () { + // it(`takes in a function, a start index, and an end index, and returns a function + // that accumulates a subset of its arguments by applying the given function to + // all elements between start and end`, function () { + // const addSecondToThird = sol.accPartial(sol.add, 1, 3); + // expect(addSecondToThird(1, 2, 4, 8)).to.deep.equal([1, 6, 8]); + // }); + // }); + // describe("accRecurse(func,initial)", function () { + // it(`does what acc does but uses recursion`, function () { + // let add = sol.accRecurse(sol.addb, 0); + // assert.equal(add(1, 2, 4), 7); + + // let mul = sol.accRecurse(sol.mulb, 1); + // assert.equal(mul(1, 2, 4), 8); + // }); + // }); + // describe("fill(num)", function () { + // it(`takes a number and returns an array with that many numbers equal to the given + // number`, function () { + // expect(sol.fill(3)).to.deep.equal([3, 3, 3]); + // }); + // }); + // describe("fillRecurse(num)", function () { + // it(`does what fill does but uses recursion`, function () { + // expect(sol.fillRecurse(3)).to.deep.equal([3, 3, 3]); + // }); + // }); + // describe("set(...args)", function () { + // it(`is given a list of arguments and returns an array with all duplicates + // removed`, function () { + // expect(sol.set(1, 1, 1, 2, 2, 2)).to.deep.equal([1, 2]); + // }); + // }); + // describe("identityf(x)", function () { + // it(`takes an argument and returns a function that returns that argument`, function () { + // assert.equal(sol.identityf(3)(), 3); + // }); + // }); + // describe("addf(a)", function () { + // it(`adds from two invocations`, function () { + // assert.equal(sol.addf(3)(4), 7); + // }); + // }); + // describe("liftf(binary)", function () { + // it(`takes a binary function, and makes it callable with two invocations`, function () { + // assert.equal(sol.liftf(sol.addb)(3)(4), 7); + // assert.equal(sol.liftf(sol.mulb)(5)(6), 30); + // }); + // }); + // describe("pure(x,y)", function () { + // it(`is a wrapper arround the impure function impure`, function () { + // expect(sol.pure(20, 5)).to.deep.equal([6, 120]); + // expect(sol.pure(25, 6)).to.deep.equal([7, 175]); + // }); + // }); + // describe("curryb(binary, a)", function () { + // it(`takes a binary function and an argument, and returns a function that can take + // a second argument`, function () { + // assert.equal(sol.curryb(sol.addb, 3)(4), 7); + // assert.equal(sol.curryb(sol.mulb, 5)(6), 30); + // }); + // }); + // describe("curry(func, ...outer)", function () { + // it(`is a curry function generalized for any amount of arguments`, function () { + // assert.equal(sol.curry(sol.add, 1, 2, 4)(4, 2, 1), 14); + // assert.equal(sol.curry(sol.sub, 1, 2, 4)(4, 2, 1), -12); + // assert.equal(sol.curry(sol.mul, 1, 2, 4)(4, 2, 1), 64); + // }); + // }); + // describe("inc(x)", function () { + // it(`shows multiple ways to create the inc function`, function () { + // assert.equal(sol.inc(5), 6); + // assert.equal(sol.inc(sol.inc(5)), 7); + // }); + // }); + // describe("twiceUnary(binary)", function () { + // it(`takes a binary function and returns a unary function that passes its argument + // to the binary function twice`, function () { + // assert.equal(sol.twiceUnary(sol.addb)(11), 22); + // assert.equal(sol.twiceUnary(sol.mulb)(11), 121); + // }); + // }); + // describe("doubl(x)", function () { + // it(`uses the function twiceUnary to create the doubl function`, function () { + // assert.equal(sol.doubl(11), 22); + // }); + // }); + // describe("square(x)", function () { + // it(`uses the function twiceUnary to create the square function`, function () { + // assert.equal(sol.square(11), 121); + // }); + // }); + // describe("twice(x)", function () { + // it(`is a twice function generalized for any amount of arguments`, function () { + // assert.equal(sol.twice(sol.add)(1, 2, 4), 14); + // }); + // }); + // describe("reverseb(binary)", function () { + // it(`reverses the arguments of a binary function`, function () { + // assert.equal(sol.reverseb(sol.subb)(3, 2), -1); + // }); + // }); + // describe("reverse(func)", function () { + // it(`is a reverse function generalized for any amount of arguments`, function () { + // assert.equal(sol.reverse(sol.sub)(1, 2, 4), 1); + // }); + // }); + // describe("composeuTwo(unary1,unary2)", function () { + // it(`takes two unary functions and returns a unary function that calls them + // both`, function () { + // assert.equal(sol.composeuTwo(sol.doubl, sol.square)(5), 100); + // }); + // }); + // describe("composeu(...funcs)", function () { + // it(`is a compose function generalized for any amount of arguments`, function () { + // assert.equal( + // sol.composeu( + // sol.doubl, + // sol.square, + // sol.identity, + // sol.curry(sol.add, 1, 2) + // )(5), + // 103 + // ); + // }); + // }); + // describe("composeb(binary1,binary2)", function () { + // it(`takes two binary functions and returns a function that calls them both`, function () { + // assert.equal(sol.composeb(sol.addb, sol.mulb)(2, 3, 7), 35); + // }); + // }); + // describe("composeTwo(func1,func2)", function () { + // it(`takes two functions and returns a function that calls them both`, function () { + // assert.equal(sol.composeTwo(sol.add, sol.square)(2, 3, 7), 144); + // }); + // }); + // describe("compose(...funcs)", function () { + // it(`takes any amount of functions and returns a function that takes any amount of + // arguments and gives them to the first function, then that result to the + // second function and so on`, function () { + // assert.equal( + // sol.compose(sol.add, sol.doubl, sol.fill, sol.max)(0, 1, 2), + // 6 + // ); + // }); + // }); + // describe("limitb(binary, lmt)", function () { + // it(`allows a binary function to be called a limited number of times`, function () { + // let addlmtb = sol.limitb(sol.addb, 1); + // assert.equal(addlmtb(3, 4), 7); + // assert.equal(addlmtb(3, 5), undefined); + // }); + // }); + // describe("limit(func, lmt)", function () { + // it(`allows a function that is generalized for any amount of arguments + // to be called a limited number of times`, function () { + // let addlmt = sol.limit(sol.add, 1); + // assert.equal(addlmt(1, 2, 4), 7); + // assert.equal(addlmt(3, 5, 9, 2), undefined); + // }); + // }); + // describe("genFrom(x)", function () { + // it(`produces a generator that will produces a series of values`, function () { + // let index = sol.genFrom(0); + // assert.equal(index.next().value, 0); + // assert.equal(index.next().value, 1); + // assert.equal(index.next().value, 2); + // }); + // }); + // describe("genTo(x)", function () { + // it(`takes a generator and an end limit, and returns a generator that will + // produce numbers up to that limit`, function () { + // let index = sol.genTo(sol.genFrom(1), 3); + // assert.equal(index.next().value, 1); + // assert.equal(index.next().value, 2); + // assert.equal(index.next().value, undefined); + // }); + // }); + // describe("genFromTo(x)", function () { + // it(`produces a generator that will produce values in a range`, function () { + // let index = sol.genFromTo(0, 3); + // assert.equal(index.next().value, 0); + // assert.equal(index.next().value, 1); + // assert.equal(index.next().value, 2); + // assert.equal(index.next().value, undefined); + // }); + // }); + // describe("elementGen(array,gen)", function () { + // it(`takes an array and a generator and returns a generator that will produce + // elements from the array`, function () { + // let ele = sol.elementGen(["a", "b", "c", "d"], sol.genFromTo(1, 3)); + // assert.equal(ele.next().value, "b"); + // assert.equal(ele.next().value, "c"); + // assert.equal(ele.next().value, undefined); + // }); + // }); + // describe("element(array,gen)", function () { + // it(`is a modified elementGen function so that the generator argument is optional. + // If a generator is not provided, then each of the elements of the array will + // be produced.`, function () { + // let ele = sol.element(["a", "b", "c", "d"]); + // assert.equal(ele.next().value, "a"); + // assert.equal(ele.next().value, "b"); + // assert.equal(ele.next().value, "c"); + // assert.equal(ele.next().value, "d"); + // assert.equal(ele.next().value, undefined); + // }); + // }); + // describe("collect(gen,array)", function () { + // it(`takes a generator and an array and produces a function that will collect the + // results in the array`, function () { + // let array = []; + // let col = sol.collect(sol.genFromTo(0, 2), array); + // assert.equal(col.next().value, 0); + // assert.equal(col.next().value, 1); + // assert.equal(col.next().value, undefined); + // expect(array).to.deep.equal([0, 1]); + // }); + // }); + // describe("filter(gen,predicate)", function () { + // it(`takes a generator and a predicate and produces a generator that produces only + // the values approved by the predicate`, function () { + // let fil = sol.filter(sol.genFromTo(0, 5), (val) => val % 3 === 0); + // assert.equal(fil.next().value, 0); + // assert.equal(fil.next().value, 3); + // assert.equal(fil.next().value, undefined); + // }); + // }); + // describe("filterTail(gen,predicate)", function () { + // it(`uses tail-recursion to perform the filtering`, function () { + // let fil = sol.filterTail(sol.genFromTo(0, 5), (val) => val % 3 === 0); + // assert.equal(fil.next().value, 0); + // assert.equal(fil.next().value, 3); + // assert.equal(fil.next().value, undefined); + // }); + // }); + // describe("concatTwo(gen1,gen2)", function () { + // it(`takes two generators and produces a generator that combines the sequences`, function () { + // let con = sol.concatTwo(sol.genFromTo(0, 3), sol.genFromTo(0, 2)); + // assert.equal(con.next().value, 0); + // assert.equal(con.next().value, 1); + // assert.equal(con.next().value, 2); + // assert.equal(con.next().value, 0); + // assert.equal(con.next().value, 1); + // assert.equal(con.next().value, undefined); + // }); + // }); + // describe("concat(...gens)", function () { + // it(`is generalized for any amount of arguments`, function () { + // let con = sol.concat( + // sol.genFromTo(0, 3), + // sol.genFromTo(0, 2), + // sol.genFromTo(5, 7) + // ); + // assert.equal(con.next().value, 0); + // assert.equal(con.next().value, 1); + // assert.equal(con.next().value, 2); + // assert.equal(con.next().value, 0); + // assert.equal(con.next().value, 1); + // assert.equal(con.next().value, 5); + // assert.equal(con.next().value, 6); + // assert.equal(con.next().value, undefined); + // }); + // }); + // describe("concatTail(...gens)", function () { + // it(`uses tail-recursion to perform the concating`, function () { + // let con = sol.concatTail( + // sol.genFromTo(0, 3), + // sol.genFromTo(0, 2), + // sol.genFromTo(5, 7) + // ); + // assert.equal(con.next().value, 0); + // assert.equal(con.next().value, 1); + // assert.equal(con.next().value, 2); + // assert.equal(con.next().value, 0); + // assert.equal(con.next().value, 1); + // assert.equal(con.next().value, 5); + // assert.equal(con.next().value, 6); + // assert.equal(con.next().value, undefined); + // }); + // }); + // describe("gensymf(symbol)", function () { + // it(`makes a function that generates unique symbols`, function () { + // let genG = sol.gensymf("G"); + // let genH = sol.gensymf("H"); + // assert.equal(genG.next().value, "G1"); + // assert.equal(genH.next().value, "H1"); + // assert.equal(genG.next().value, "G2"); + // assert.equal(genH.next().value, "H2"); + // }); + // }); + // describe("gensymff(unary, seed)", function () { + // it(`takes a unary function and a seed and returns a gensymf`, function () { + // let gensymf = sol.gensymff(sol.inc, 0); + // let genG = gensymf("G"); + // let genH = gensymf("H"); + // assert.equal(genG.next().value, "G1"); + // assert.equal(genH.next().value, "H1"); + // assert.equal(genG.next().value, "G2"); + // assert.equal(genH.next().value, "H2"); + // }); + // }); + // describe("fibonaccif(first, second)", function () { + // it(`returns a generator that will return the next fibonacci number`, function () { + // let fib = sol.fibonaccif(0, 1); + // assert.equal(fib.next().value, 0); + // assert.equal(fib.next().value, 1); + // assert.equal(fib.next().value, 1); + // assert.equal(fib.next().value, 2); + // assert.equal(fib.next().value, 3); + // assert.equal(fib.next().value, 5); + // assert.equal(fib.next().value, 8); + // }); + // }); + // describe("counter(i)", function () { + // it(`returns an object containing two functions that implement an up/down counter, + // hiding the counter`, function () { + // let obj = sol.counter(10); + // let { up, down } = obj; + // assert.equal(up(), 11); + // assert.equal(down(), 10); + // assert.equal(down(), 9); + // assert.equal(up(), 10); + // }); + // }); + // describe("revocableb(binary)", function () { + // it(`takes a binary function, and returns an object containing an invoke function + // that can invoke a function and a revoke function that disables the invoke + // function`, function () { + // let rev = sol.revocableb(sol.addb); + // assert.equal(rev.invoke(3, 4), 7); + // rev.revoke(); + // assert.equal(rev.invoke(5, 7), undefined); + // }); + // }); + // describe("revocable(binary)", function () { + // it(`takes a function that is generalized for any amount of arguments, and returns + // an object containing an invoke function that can invoke a function and a revoke + // function that disables the invoke function`, function () { + // let rev = sol.revocable(sol.add); + // assert.equal(rev.invoke(3, 4), 7); + // rev.revoke(); + // assert.equal(rev.invoke(5, 7), undefined); + // }); + // }); + // describe("extract(array,prop)", function () { + // it(`takes an array of objects and an object property name and converts each object + // in the array by extracting that property`, function () { + // let people = [{ name: "john" }, { name: "bob" }]; + // expect(sol.extract(people, "name")).to.deep.equal(["john", "bob"]); + // }); + // }); + // describe("m(value,source)", function () { + // it(`takes a value and an optional source string and returns them in an object`, function () { + // expect(sol.m(1)).to.deep.equal({ value: 1, source: "1" }); + // expect(sol.m(Math.PI, "pi")).to.deep.equal({ + // value: Math.PI, + // source: "pi", + // }); + // }); + // }); + // describe("addmTwo(m1,m2)", function () { + // it(`adds two m objects and returns an m object`, function () { + // expect(sol.addmTwo(sol.m(3), sol.m(4))).to.deep.equal({ + // value: 7, + // source: "(3+4)", + // }); + // expect(sol.addmTwo(sol.m(1), sol.m(Math.PI, "pi"))).to.deep.equal({ + // value: Math.PI + 1, + // source: "(1+pi)", + // }); + // }); + // }); + // describe("addm(...ms)", function () { + // it(`is a function that is generalized for any amount of arguments that adds m + // objects and returns an m object`, function () { + // expect(sol.addm(sol.m(1), sol.m(2), sol.m(4))).to.deep.equal({ + // value: 7, + // source: "(1+2+4)", + // }); + // }); + // }); + // describe("liftmbM(binary, op)", function () { + // it(`takes a binary function and a string and returns a function that acts on m + // objects`, function () { + // expect(sol.liftmbM(sol.addb, "+")(sol.m(3), sol.m(4))).to.deep.equal({ + // value: 7, + // source: "(3+4)", + // }); + // expect(sol.liftmbM(sol.mulb, "*")(sol.m(3), sol.m(4))).to.deep.equal({ + // value: 12, + // source: "(3*4)", + // }); + // }); + // }); + // describe("liftmb(binary, op)", function () { + // it(`is a modified function liftmbM that can accept arguments that are either numbers + // or m objects`, function () { + // expect(sol.liftmb(sol.addb, "+")(3, 4)).to.deep.equal({ + // value: 7, + // source: "(3+4)", + // }); + // }); + // }); + // describe("liftm(func, op)", function () { + // it(`is a modified function liftmbM that is generalized for any amount of arguments + // that can accept arguments that are either numbers or m objects`, function () { + // expect(sol.liftm(sol.addb, "+")(sol.m(3), sol.m(4))).to.deep.equal({ + // value: 7, + // source: "(3+4)", + // }); + // expect(sol.liftm(sol.mulb, "*")(sol.m(3), sol.m(4))).to.deep.equal({ + // value: 12, + // source: "(3*4)", + // }); + // expect(sol.liftm(sol.mulb, "*")(3, 4)).to.deep.equal({ + // value: 12, + // source: "(3*4)", + // }); + // }); + // }); + // describe("exp(value)", function () { + // it(`evaluates simple array expressions`, function () { + // assert.equal(sol.exp([sol.mul, 1, 2, 4]), 8); + // assert.equal(sol.exp(42), 42); + // }); + // }); + // describe("expn(value)", function () { + // it(`is a modified exp that can evaluate nested array expressions`, function () { + // assert.equal( + // sol.expn([Math.sqrt, [sol.add, [sol.square, 3], [sol.square, 4]]]), + // 5 + // ); + // // assert.equal(sol.expn(34), 34); + // }); + // }); + // describe("addg(value)", function () { + // it(`adds from many invocations, until it sees an empty invocation`, function () { + // assert.equal(sol.addg(), undefined); + // assert.equal(sol.addg(2)(), 2); + // assert.equal(sol.addg(2)(7)(), 9); + // assert.equal(sol.addg(3)(0)(4)(), 7); + // assert.equal(sol.addg(1)(2)(4)(8)(), 15); + // }); + // }); + // describe("liftg(value)", function () { + // it(`will take a binary function and apply it to many invocations`, function () { + // assert.equal(sol.liftg(sol.mulb)(), undefined); + // assert.equal(sol.liftg(sol.mulb)(3)(), 3); + // assert.equal(sol.liftg(sol.mulb)(3)(0)(4)(), 0); + // assert.equal(sol.liftg(sol.mulb)(1)(2)(4)(8)(), 64); + // }); + // }); + // describe("arrayg(value)", function () { + // it(`will build an array from many invocations`, function () { + // expect(sol.arrayg()).to.deep.equal([]); + // expect(sol.arrayg(3)()).to.deep.equal([3]); + // expect(sol.arrayg(3)(4)(5)()).to.deep.equal([3, 4, 5]); + // }); + // }); + // describe("continuizeu(unary)", function () { + // beforeEach(function () { + // this.sinon.stub(console, "log"); + // }); + + // it(`takes a unary function and returns a function that takes a callback and an argument`, function () { + // sol.continuizeu(Math.sqrt)(console.log, 81); + // expect(console.log.calledOnce).to.be.true; + // expect(console.log.calledWith(9)).to.be.true; + // }); + // }); + // describe("continuize(any)", function () { + // beforeEach(function () { + // this.sinon.stub(console, "log"); + // }); + + // it(`takes a function and returns a function that takes a callback and an argument`, function () { + // sol.continuize(sol.mul)(console.log, 81, 4, 2); + // expect(console.log.calledOnce).to.be.true; + // expect(console.log.calledWith(648)).to.be.true; + // }); + // }); + // describe("vector()", function () { + // it(`is an array wrapper object with methods get, store, and append, such that an + // attacker cannot get access to the private array`, function () { + // let v = sol.vector(); + // v.append(7); + // v.store(1, 8); + // assert.equal(v.get(0), 7); + // assert.equal(v.get(1), 8); + // }); + // }); + // describe("exploitVector()", function () { + // it(`accesses array outside of vector`, function () { + // let v = sol.vector(); + // v.append(1); + // v.append(2); + // expect(sol.exploitVector(v)).to.deep.equal([1, 2]); + // }); + // }); + // describe("vectorSafe()", function () { + // it(`can't access array outside of vector`, function () { + // let v = sol.vectorSafe(); + // v.append(1); + // v.append(2); + // expect(sol.exploitVector(v)).to.deep.equal(undefined); + // }); + // }); + // describe("pubsub()", function () { + // beforeEach(function () { + // this.sinon.stub(console, "log"); + // }); + + // it(`makes a publish/subscribe object. It will reliably deliver all publications + // to all subscribers in the right order`, function () { + // let ps = sol.pubsub(); + // ps.subscribe(console.log); + // ps.publish("It works!"); + + // expect(console.log.calledOnce).to.be.true; + // expect(console.log.calledWith("It works!")).to.be.true; + // }); + // }); + // describe("mapRecurse(array, predicate)", function () { + // it(`performs a transformation for each element of a given array, recursively`, function () { + // expect(sol.mapRecurse([1, 2, 3, 4], (x) => x * 2)).to.deep.equal([ + // 2, 4, 6, 8, + // ]); + // }); + // }); + // describe("filterRecurse(array, predicate)", function () { + // it(`takes in an array and a predicate function and returns a new array by + // filtering out all items using the predicate, recursively.`, function () { + // expect(sol.filterRecurse([1, 2, 3, 4], (x) => x % 2 === 0)).to.deep.equal( + // [2, 4] + // ); + // }); + // }); +}); From da0ef9d2212e429279f4bb2a97f123769c3b00d1 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Tue, 13 Feb 2024 22:50:36 +0100 Subject: [PATCH 02/15] [ADD] multiple functions + positive and negative tests - mulb, - minb, - maxb, - add, - sub, - mul, - min, - max, - addRecurse, - mulRecurse, - minRecurse, - maxRecurse, - not, - acc, - accPartial, - accRecurse --- Solutions/Bltzz_solution.js | 265 ++++++++++++++++++++++++++- test/Bltzz_tests.js | 354 +++++++++++++++++++++++++++++------- 2 files changed, 549 insertions(+), 70 deletions(-) diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index 1f09252..889074a 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -3,11 +3,29 @@ * */ -function _check (x, y) { +function _check(x, y) { if (typeof x !== 'number') - throw new TypeError(`${x} is not a number`); - if (typeof y !== 'number') - throw new TypeError(`${y} is not a number`); + throw new TypeError(`${x} is not a number`); + if (typeof y !== 'number') + throw new TypeError(`${y} is not a number`); +} + +function _checkSingle(x) { + if (typeof x !== 'number') + throw new TypeError(`${x} is not a number`); +} + +function _checkArray(...nums) { + if (nums.length === 0) { + throw new Error('No arguments provided.'); + } + nums.forEach(x => _checkSingle(x)) +} + +function _checkFunction(func){ + if (typeof func !== 'function') + throw new TypeError('Input must be a function'); + } /** @@ -15,7 +33,7 @@ function _check (x, y) { * @param {*} x - Any value * @returns {*} - The same value */ -function identity(x){ +function identity(x) { return x } @@ -36,7 +54,7 @@ function addb(a, b) { * @param {*} y - Any value * @returns {*} - x minus y */ -function subb(a, b) { +function subb(a, b) { _check(a, b) return a - b } @@ -47,15 +65,246 @@ function subb(a, b) { * @param {*} y - Any value * @returns {*} - x times y */ -function mulb(a, b) { +function mulb(a, b) { _check(a, b) return a * b } +/** + * minb(a, b) ⇒ number + * @param {*} x - Any value + * @param {*} y - Any value + * @returns {*} - The smaller value + */ +function minb(a, b) { + _check(a, b) + return a < b ? a : b +} + +/** + * maxb(a, b) ⇒ number + * @param {*} x - Any value + * @param {*} y - Any value + * @returns {*} - The bigger value + */ +function maxb(a, b) { + _check(a, b) + return a > b ? a : b +} + +/** + * add(...nums) ⇒ number + * @param {...number} nums - Any number of numeric values + * @returns {number} - The sum of all the numeric values + */ +function add(...nums) { + _checkArray(...nums) + let sum = 0; + for (let i = 0; i < nums.length; i++) sum += nums[i] + // Alternative: + // Use build-in reduce function + // return nums.reduce((sum, num) => sum + num, 0); + return sum +} + +/** + * sub(...nums) ⇒ number + * @param {...number} nums - Any number of numeric values + * @returns {number} - The result of subtracting all the numeric values - taking first element as starting point + */ +function sub(...nums) { + _checkArray(...nums) + let result = nums[0]; + for (let i = 1; i < nums.length; i++) result -= nums[i]; + // Alternative: + // Use built-in reduce function + // return nums.reduce((result, num) => result - num); + return result; +} + +/** + * mul(...nums) ⇒ number + * @param {...number} nums - Any number of numeric values + * @returns {number} - The result of mulitplying all the numeric values + */ +function mul(...nums) { + _checkArray(...nums) + let result = 1; + for (let i = 0; i < nums.length; i++) result *= nums[i]; + // Alternative: + // Use built-in reduce function + // return nums.reduce((result, num) => result * num); + return result; +} + +/** + * min(...nums) ⇒ number + * @param {...number} nums - Any number of numeric values + * @returns {number} - The smallest of the numeric values + */ +function min(...nums) { + _checkArray(...nums) + let result = nums[0]; + for (let i = 0; i < nums.length; i++) (result > nums[i + 1]) ? result = nums[i + 1] : result + // Alternative: + // Use built-in Math.min function + //return Math.min(...nums); + return result; +} + +/** + * max(...nums) ⇒ number + * @param {...number} nums - Any number of numeric values + * @returns {number} - The biggest of the numeric values + */ +function max(...nums) { + _checkArray(...nums) + let result = nums[0]; + for (let i = 0; i < nums.length; i++) (result < nums[i + 1]) ? result = nums[i + 1] : result + // Alternative: + // Use built-in Math.max function + //return Math.max(...nums); + return result; +} + +/** + * addRecurse(...nums) ⇒ number + * @param {...number} nums - Any number of numeric values + * @returns {number} - The sum of the numeric values + */ +function addRecurse(...nums) { + nums.forEach(x => _checkSingle(x)) + if (nums.length === 0) + return 0; + return nums[0] + addRecurse(...nums.slice(1)); +} + +/** + * mulRecurse(...nums) ⇒ number + * @param {...number} nums - Any number of numeric values + * @returns {number} - The product of the numeric values + */ +function mulRecurse(...nums) { + nums.forEach(x => _checkSingle(x)) + if (nums.length === 0) + return 1; + return nums[0] * mulRecurse(...nums.slice(1)); +} + +/** + * minRecurse(...nums) ⇒ number + * @param {...number} nums - Any number of numeric values + * @returns {number} - The smallest of the numeric values + */ +function minRecurse(...nums) { + nums.forEach(x => _checkSingle(x)) + // Base case: return -Infinity if no arguments are provided + if (nums.length === 0) return Infinity; + // Base case: return the single value if only one argument is provided + if (nums.length === 1) return nums[0]; + return nums[0] < minRecurse(...nums.slice(1)) ? nums[0] : minRecurse(...nums.slice(1)); +} + +/** + * maxRecurse(...nums) ⇒ number + * @param {...number} nums - Any number of numeric values + * @returns {number} - The biggest of the numeric values + */ +function maxRecurse(...nums) { + nums.forEach(x => _checkSingle(x)) + // Base case: return -Infinity if no arguments are provided + if (nums.length === 0) return -Infinity; + // Base case: return the single value if only one argument is provided + if (nums.length === 1) return nums[0]; + // Recursive case: compare the first number with the maximum of the rest + return nums[0] > maxRecurse(...nums.slice(1)) ? nums[0] : maxRecurse(...nums.slice(1)); +} + +/** + * not(func) ⇒ function + * @param {func} function - Any function + * @returns {func} - The inverse res of the function + */ +function not(func){ + _checkFunction(func) + return function (...args) { + return !func(...args); + }; +} + +/** + * acc(func, initial) ⇒ function + * @param {func} function - Any function + * @returns {func} - returns a number that runs the initial function on each argument, accumulating the result + */ +function acc(func, initial){ + _checkFunction(func) + _checkSingle(initial) + return function (...args) { + let x = initial; + for (let i of args) { + x = func(x, i); + } + return x; + }; +} + +/** + * accPartial(func, start, end) ⇒ function + * @param {func} function - Any function + * @returns {func} - returns a function that accumulates a subset of its arguments by applying the given function to + all elements between start and end incl. the stuff before start and after end + */ +function accPartial(func, start, end){ + _checkFunction(func) + _checkArray(start, end) + return function (...args) { + const subset = args.slice(start, end); + // return args.slice(0, start) + func(...subset) + args.slice(end, args.length); + return [...args.slice(0, start), func(...subset), ...args.slice(end)]; + }; +} + +/** + * accRecurse(func, initial) ⇒ function + * @param {func} function - Any function + * @returns {func} - Same as acc, but recursively + */ +function accRecurse(func, initial){ + _checkFunction(func) + _checkArray(initial) + return function (...args) { + if (args.length === 0) { + return initial; + } + const first = args[0]; + const rest = args.slice(1); + // same as + // const [first, ...rest] = args; + const result = func(initial, first); + + return acc(func, result)(...rest); + }; +} module.exports = { identity, addb, subb, - mulb + mulb, + minb, + maxb, + add, + sub, + mul, + min, + max, + addRecurse, + mulRecurse, + minRecurse, + maxRecurse, + not, + acc, + accPartial, + accRecurse } diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index f4e6073..1d3347d 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -1,8 +1,17 @@ -const assert = require('chai').assert; -const expect = require('chai').expect; +const chai = require('chai'); +const sinonChai = require('sinon-chai'); + +require('mocha-sinon'); + +const assert = chai.assert; +const expect = chai.expect; + +chai.use(sinonChai); + const filename = 'Bltzz_solution'; const sol = require('../Solutions/' + filename); -require('mocha-sinon'); + + describe("JS_Fun_Practice", function () { @@ -65,7 +74,7 @@ describe("JS_Fun_Practice", function () { expect(() => sol.mulb('40', 2)).to.throw(TypeError); expect(() => sol.mulb([], 2)).to.throw(TypeError); expect(() => sol.mulb({}, 2)).to.throw(TypeError); - expect(() => sol.mulb("sfs", "b")).to.throw(TypeError); + expect(() => sol.mulb("foo", "bar")).to.throw(TypeError); }); }); @@ -78,109 +87,330 @@ describe("JS_Fun_Practice", function () { }); it('should throw a TypeError if arguments are not numbers', () => { - expect(() => sol.mulb(40, '2')).to.throw(TypeError); - expect(() => sol.mulb(40, [])).to.throw(TypeError); - expect(() => sol.mulb(40, {})).to.throw(TypeError); - expect(() => sol.mulb('40', 2)).to.throw(TypeError); - expect(() => sol.mulb([], 2)).to.throw(TypeError); - expect(() => sol.mulb({}, 2)).to.throw(TypeError); - expect(() => sol.mulb("sfs", "b")).to.throw(TypeError); + expect(() => sol.minb(40, '2')).to.throw(TypeError); + expect(() => sol.minb(40, [])).to.throw(TypeError); + expect(() => sol.minb(40, {})).to.throw(TypeError); + expect(() => sol.minb('40', 2)).to.throw(TypeError); + expect(() => sol.minb([], 2)).to.throw(TypeError); + expect(() => sol.minb({}, 2)).to.throw(TypeError); + expect(() => sol.minb("foo", "bar")).to.throw(TypeError); }); }); - + describe("maxb(a,b)", function () { + it("takes two numbers and returns the larger one", function () { assert.equal(sol.maxb(3, 4), 4); }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.maxb(40, '2')).to.throw(TypeError); + expect(() => sol.maxb(40, [])).to.throw(TypeError); + expect(() => sol.maxb(40, {})).to.throw(TypeError); + expect(() => sol.maxb('40', 2)).to.throw(TypeError); + expect(() => sol.maxb([], 2)).to.throw(TypeError); + expect(() => sol.maxb({}, 2)).to.throw(TypeError); + expect(() => sol.maxb("foo", "bar")).to.throw(TypeError); + }); + }); + + describe("add(...nums)", function () { + it("is an add fuction that is generalized for any amount of arguments", function () { assert.equal(sol.add(1, 2, 4), 7); }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.add(40, 41, '2')).to.throw(TypeError); + expect(() => sol.add(40, [], 12)).to.throw(TypeError); + expect(() => sol.add(40, {}, 42)).to.throw(TypeError); + expect(() => sol.add('40', 2)).to.throw(TypeError); + expect(() => sol.add([], 42, 2)).to.throw(TypeError); + expect(() => sol.add({}, 2)).to.throw(TypeError); + expect(() => sol.add("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.add(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); + expect(() => sol.add()).to.throw('No arguments provided.'); + }); + }); + + describe("sub(...nums)", function () { + it("is a sub fuction that is generalized for any amount of arguments", function () { assert.equal(sol.sub(1, 2, 4), -5); }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.sub(40, 41, '2')).to.throw(TypeError); + expect(() => sol.sub(40, [], 12)).to.throw(TypeError); + expect(() => sol.sub(40, {}, 42)).to.throw(TypeError); + expect(() => sol.sub('40', 2)).to.throw(TypeError); + expect(() => sol.sub([], 42, 2)).to.throw(TypeError); + expect(() => sol.sub({}, 2)).to.throw(TypeError); + expect(() => sol.sub("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.sub(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); + expect(() => sol.sub()).to.throw('No arguments provided.'); + }); + }); + + describe("mul(...nums)", function () { + it("is a mul fuction that is generalized for any amount of arguments", function () { assert.equal(sol.mul(1, 2, 4), 8); }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.mul(40, 41, '2')).to.throw(TypeError); + expect(() => sol.mul(40, [], 12)).to.throw(TypeError); + expect(() => sol.mul(40, {}, 42)).to.throw(TypeError); + expect(() => sol.mul('40', 2)).to.throw(TypeError); + expect(() => sol.mul([], 42, 2)).to.throw(TypeError); + expect(() => sol.mul({}, 2)).to.throw(TypeError); + expect(() => sol.mul("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.mul(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); + expect(() => sol.mul()).to.throw('No arguments provided.'); + }); + }); + + describe("min(...nums)", function () { + it("is a min fuction that is generalized for any amount of arguments", function () { assert.equal(sol.min(1, 2, 4), 1); }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.min(40, 41, '2')).to.throw(TypeError); + expect(() => sol.min(40, [], 12)).to.throw(TypeError); + expect(() => sol.min(40, {}, 42)).to.throw(TypeError); + expect(() => sol.min('40', 2)).to.throw(TypeError); + expect(() => sol.min([], 42, 2)).to.throw(TypeError); + expect(() => sol.min({}, 2)).to.throw(TypeError); + expect(() => sol.min("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.min(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); + expect(() => sol.min()).to.throw('No arguments provided.'); + }); + }); + + describe("max(...nums)", function () { + it("is a max fuction that is generalized for any amount of arguments", function () { - assert.equal(sol.max(1, 2, 4), 4); + assert.equal(sol.max(1, 6, 4), 6); }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.max(40, 41, '2')).to.throw(TypeError); + expect(() => sol.max(40, [], 12)).to.throw(TypeError); + expect(() => sol.max(40, {}, 42)).to.throw(TypeError); + expect(() => sol.max('40', 2)).to.throw(TypeError); + expect(() => sol.max([], 42, 2)).to.throw(TypeError); + expect(() => sol.max({}, 2)).to.throw(TypeError); + expect(() => sol.max("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.max(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); + expect(() => sol.max()).to.throw('No arguments provided.'); + }); + }); + + describe("addRecurse(...nums)", function () { + it("is an add fuction that is generalized but uses recursion", function () { assert.equal(sol.addRecurse(1, 2, 4), 7); + assert.equal(sol.addRecurse(), 0); + }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.addRecurse(40, 41, '2')).to.throw(TypeError); + expect(() => sol.addRecurse(40, [], 12)).to.throw(TypeError); + expect(() => sol.addRecurse(40, {}, 42)).to.throw(TypeError); + expect(() => sol.addRecurse('40', 2)).to.throw(TypeError); + expect(() => sol.addRecurse([], 42, 2)).to.throw(TypeError); + expect(() => sol.addRecurse({}, 2)).to.throw(TypeError); + expect(() => sol.addRecurse("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.addRecurse(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); }); + }); + + describe("mulRecurse(...nums)", function () { + it("is a mul fuction that is generalized but uses recursion", function () { assert.equal(sol.mulRecurse(1, 2, 4), 8); }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.mulRecurse(40, 41, '2')).to.throw(TypeError); + expect(() => sol.mulRecurse(40, [], 12)).to.throw(TypeError); + expect(() => sol.mulRecurse(40, {}, 42)).to.throw(TypeError); + expect(() => sol.mulRecurse('40', 2)).to.throw(TypeError); + expect(() => sol.mulRecurse([], 42, 2)).to.throw(TypeError); + expect(() => sol.mulRecurse({}, 2)).to.throw(TypeError); + expect(() => sol.mulRecurse("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.mulRecurse(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); + }); + + }); + describe("minRecurse(...nums)", function () { + + // TODO: Nice addon: Count the function calls by wrapping recursive method in class function + // See: https://stackoverflow.com/questions/51699584/how-to-spy-on-a-recursive-function-in-javascript/51699585#51699585 + it("is a min fuction that is generalized but uses recursion", function () { + assert.equal(sol.minRecurse(1, 2, 4), 1); + assert.equal(sol.minRecurse(3, 2, 1), 1); + }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.minRecurse(40, 41, '2')).to.throw(TypeError); + expect(() => sol.minRecurse(40, [], 12)).to.throw(TypeError); + expect(() => sol.minRecurse(40, {}, 42)).to.throw(TypeError); + expect(() => sol.minRecurse('40', 2)).to.throw(TypeError); + expect(() => sol.minRecurse([], 42, 2)).to.throw(TypeError); + expect(() => sol.minRecurse({}, 2)).to.throw(TypeError); + expect(() => sol.minRecurse("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.minRecurse(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); + }); + + }); + + + describe("maxRecurse(...nums)", function () { + + it("is a max fuction that is generalized but uses recursion", function () { + assert.equal(sol.maxRecurse(1, 2, 4), 4); + }); + + it('should throw a TypeError if arguments are not numbers', () => { + expect(() => sol.maxRecurse(40, 41, '2')).to.throw(TypeError); + expect(() => sol.maxRecurse(40, [], 12)).to.throw(TypeError); + expect(() => sol.maxRecurse(40, {}, 42)).to.throw(TypeError); + expect(() => sol.maxRecurse('40', 2)).to.throw(TypeError); + expect(() => sol.maxRecurse([], 42, 2)).to.throw(TypeError); + expect(() => sol.maxRecurse({}, 2)).to.throw(TypeError); + expect(() => sol.maxRecurse("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.maxRecurse(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); + }); + + }); + + + describe("not(func)", function () { + + it("takes a function and returns the negation of its result", function () { + const isOdd = (x) => x % 2 === 1; + const isEven = sol.not(isOdd); + assert.equal(isEven(1), false); + assert.equal(isEven(2), true); + }); + + it('should throw a TypeError if arguments are not a function', () => { + expect(() => sol.not(40, 41, '2')).to.throw(TypeError); + }); + + }); + + + describe("acc(func,initial)", function () { + + it(`takes a function and an initial value and returns a function + that runs the initial function on each argument, accumulating the result`, function () { + let add = sol.acc(sol.addb, 0); + assert.equal(add(1, 2, 4), 7); + + let mul = sol.acc(sol.mulb, 1); + assert.equal(mul(1, 2, 4), 8); + }); + + it('should throw a TypeError if first arguments are not a function', () => { + expect(() => sol.acc(40, 41, '2')).to.throw(TypeError); + }); + + it('should throw a TypeError if second argument are not a number', () => { + expect(() => sol.acc(sol.addb, "ds")).to.throw(TypeError); + }); + + }); + + + describe("accPartial(func,start,end)", function () { + + it(`takes in a function, a start index, and an end index, and returns a function + that accumulates a subset of its arguments by applying the given function to + all elements between start and end`, function () { + const addSecondToThird = sol.accPartial(sol.add, 1, 3); + expect(addSecondToThird(1, 2, 4, 8)).to.deep.equal([1, 6, 8]); + + const subSecondToThird = sol.accPartial(sol.sub, 1, 3); + expect(subSecondToThird(1, 2, 4, 8)).to.deep.equal([1, -2, 8]); + + const subSecondToFourth = sol.accPartial(sol.sub, 1, 4); + expect(subSecondToFourth(1, 2, 4, 6, 8)).to.deep.equal([1, -8, 8]); + }); + + it('should throw a TypeError if first arguments are not a function', () => { + expect(() => sol.accPartial(40, 41, '2')).to.throw(TypeError); + }); + + it('should throw a TypeError if second argument are not a number', () => { + expect(() => sol.accPartial(sol.addb, "ds")).to.throw(TypeError); + }); + + }); + + + describe("accRecurse(func,initial)", function () { + + it(`does what acc does but uses recursion`, function () { + let add = sol.accRecurse(sol.addb, 0); + assert.equal(add(1, 2, 4), 7); + + let mul = sol.accRecurse(sol.mulb, 1); + assert.equal(mul(1, 2, 4), 8); + }); + + it('should throw a TypeError if first arguments are not a function', () => { + expect(() => sol.accRecurse(40, 41, '2')).to.throw(TypeError); + }); + + it('should throw a TypeError if second argument are not a number', () => { + expect(() => sol.accRecurse(sol.addb, "ds")).to.throw(TypeError); + }); + + }); + + describe("fill(num)", function () { + + it(`takes a number and returns an array with that many numbers equal to the given + number`, function () { + expect(sol.fill(3)).to.deep.equal([3, 3, 3]); + }); + + it('should throw a TypeError if second argument are not a number', () => { + expect(() => sol.fill(sol.addb)).to.throw(TypeError); + expect(() => sol.fill('40')).to.throw(TypeError); + expect(() => sol.fill([])).to.throw(TypeError); + expect(() => sol.fill([], 12)).to.throw(TypeError); + expect(() => sol.fill({})).to.throw(TypeError); + expect(() => sol.fill('40', 2)).to.throw(TypeError); + expect(() => sol.fill({}, 2)).to.throw(TypeError); + expect(() => sol.fill("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.fill(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); + }); + }); - // describe("minRecurse(...nums)", function () { - // it("is a min fuction that is generalized but uses recursion", function () { - // assert.equal(sol.minRecurse(1, 2, 4), 1); - // }); - // }); - // describe("maxRecurse(...nums)", function () { - // it("is a max fuction that is generalized but uses recursion", function () { - // assert.equal(sol.maxRecurse(1, 2, 4), 4); - // }); - // }); - // describe("not(func)", function () { - // it("takes a function and returns the negation of its result", function () { - // const isOdd = (x) => x % 2 === 1; - // const isEven = sol.not(isOdd); - // assert.equal(isEven(1), false); - // assert.equal(isEven(2), true); - // }); - // }); - // describe("acc(func,initial)", function () { - // it(`takes a function and an initial value and returns a function - // that runs the initial function on each argument, accumulating the result`, function () { - // let add = sol.acc(sol.addb, 0); - // assert.equal(add(1, 2, 4), 7); - // let mul = sol.acc(sol.mulb, 1); - // assert.equal(mul(1, 2, 4), 8); - // }); - // }); - // describe("accPartial(func,start,end)", function () { - // it(`takes in a function, a start index, and an end index, and returns a function - // that accumulates a subset of its arguments by applying the given function to - // all elements between start and end`, function () { - // const addSecondToThird = sol.accPartial(sol.add, 1, 3); - // expect(addSecondToThird(1, 2, 4, 8)).to.deep.equal([1, 6, 8]); - // }); - // }); - // describe("accRecurse(func,initial)", function () { - // it(`does what acc does but uses recursion`, function () { - // let add = sol.accRecurse(sol.addb, 0); - // assert.equal(add(1, 2, 4), 7); - // let mul = sol.accRecurse(sol.mulb, 1); - // assert.equal(mul(1, 2, 4), 8); - // }); - // }); - // describe("fill(num)", function () { - // it(`takes a number and returns an array with that many numbers equal to the given - // number`, function () { - // expect(sol.fill(3)).to.deep.equal([3, 3, 3]); - // }); - // }); // describe("fillRecurse(num)", function () { // it(`does what fill does but uses recursion`, function () { // expect(sol.fillRecurse(3)).to.deep.equal([3, 3, 3]); From c0b87c74c203e11f28650b39120f67a66dcc7c2e Mon Sep 17 00:00:00 2001 From: Bltzz Date: Tue, 13 Feb 2024 22:51:24 +0100 Subject: [PATCH 03/15] [EDIT] packages update sinon-chai dependency + version --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 213c679..7c6a34d 100644 --- a/package.json +++ b/package.json @@ -21,5 +21,8 @@ "mocha": "^10.2.0", "mocha-sinon": "^2.1.2", "sinon": "^9.0.2" + }, + "dependencies": { + "sinon-chai": "^3.7.0" } } From f7035f5ceaf1f7a4105b01d02f14a2367adf1e16 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Tue, 13 Feb 2024 23:05:27 +0100 Subject: [PATCH 04/15] [ADD] fill function --- Solutions/Bltzz_solution.js | 15 +++++++++++++-- test/Bltzz_tests.js | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index 889074a..5bfc0f0 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -11,7 +11,7 @@ function _check(x, y) { } function _checkSingle(x) { - if (typeof x !== 'number') + if (typeof x !== 'number' || x === undefined) throw new TypeError(`${x} is not a number`); } @@ -287,6 +287,16 @@ function accRecurse(func, initial){ }; } +/** + * fill(num) ⇒ array + * @param {func} function - Any function + * @returns {func} - The inverse of the function + */ +function fill(num) { + _checkSingle(num) + return Array(num).fill(num) +} + module.exports = { identity, addb, @@ -306,5 +316,6 @@ module.exports = { not, acc, accPartial, - accRecurse + accRecurse, + fill } diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index 1d3347d..748f7f0 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -397,6 +397,7 @@ describe("JS_Fun_Practice", function () { }); it('should throw a TypeError if second argument are not a number', () => { + expect(() => sol.fill()).to.throw(TypeError); expect(() => sol.fill(sol.addb)).to.throw(TypeError); expect(() => sol.fill('40')).to.throw(TypeError); expect(() => sol.fill([])).to.throw(TypeError); @@ -405,7 +406,6 @@ describe("JS_Fun_Practice", function () { expect(() => sol.fill('40', 2)).to.throw(TypeError); expect(() => sol.fill({}, 2)).to.throw(TypeError); expect(() => sol.fill("foo", "bar", 4)).to.throw(TypeError); - expect(() => sol.fill(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); }); }); From ce02d0757884c8e951852722f02c17cbe8f5fb86 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Thu, 15 Feb 2024 22:48:33 +0100 Subject: [PATCH 05/15] [ADD] seven more functions (see descr) - [ADD] func + test for: fill, - [ADD] func + test for: fillRecurse, - [ADD] func + test for: set, - [ADD] func + test for: identityf, - [ADD] func + test for: addf, - [ADD] func + test for: liftf, - [ADD] func + test for: pure --- Solutions/Bltzz_solution.js | 109 +++++++++++++++++++++++++++-- test/Bltzz_tests.js | 134 +++++++++++++++++++++++++++--------- 2 files changed, 206 insertions(+), 37 deletions(-) diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index 5bfc0f0..891e166 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -16,7 +16,7 @@ function _checkSingle(x) { } function _checkArray(...nums) { - if (nums.length === 0) { + if (nums.length === 0 || nums === undefined) { throw new Error('No arguments provided.'); } nums.forEach(x => _checkSingle(x)) @@ -289,14 +289,109 @@ function accRecurse(func, initial){ /** * fill(num) ⇒ array - * @param {func} function - Any function - * @returns {func} - The inverse of the function + * @param {num} function - Any number + * @returns {Array} - An num long array filled with num */ function fill(num) { _checkSingle(num) return Array(num).fill(num) } +/** + * fillRecurse(num) ⇒ array +* @param {num} function - Any number + * @returns {Array} - An num long array filled with num + */ +function fillRecurse(num, result = []) { + _checkSingle(num) + // Break Condition: if num is 0, return the result array + if (num === 0) { + return result; + } + // Add the given number to the result array + // as we decrease the number to make recursion possible, + // add length of the result to always add the same number. + result.push(num + result.length); + + // Call fillRecurse recursively with num - 1 + return fillRecurse(num - 1, result); +} + +/** + * set(...args) ⇒ array + * @param {num} function - Any number + * @returns {Array} - An num long array filled with num + */ +function set(...args) { + _checkArray(...args) + return Array.from(new Set(args)) +} + + +/** + * identityf(x) ⇒ function + * @param {num} function - Any number + * @returns {Array} - An num long array filled with num + */ +function identityf(x) { + return function() {return x} +} + +/** + * identityf(x) ⇒ function + * @param {x} - Anything + * @returns {function(x)} - A function that returns x + */ +function identityf(x) { + return function() {return x} +} + +/** + * addf(a) ⇒ function + * @param {a} - Any number + * @returns {function} - A function that adds from two invocations + */ +function addf(a) { + _checkSingle(a); + return function(b) { + _checkSingle(b) + return a + b; + }; +} + +/** + * liftf(binary) ⇒ function + * @param {binary}function - Any binary function + * @returns {Array} - An num long array filled with num + */ +function liftf(binary) { + _checkFunction(binary) + return function(a){ + _checkSingle(a) + return function(b){ + _checkSingle(b) + return binary(a, b); + }; + }; +} + +/** + * pure(x, y) ⇒ array + * @param {x} - Any number + * @param {y} - Any number + * @returns {Array} - An num long array filled with num + */ +function pure(x, y) { + _check(x, y) + var z; + function impure(x) { + y++; + z = x * y; + } + impure(x) + return [y, z] +} + module.exports = { identity, addb, @@ -317,5 +412,11 @@ module.exports = { acc, accPartial, accRecurse, - fill + fill, + fillRecurse, + set, + identityf, + addf, + liftf, + pure } diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index 748f7f0..6f3079b 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -411,39 +411,107 @@ describe("JS_Fun_Practice", function () { }); - // describe("fillRecurse(num)", function () { - // it(`does what fill does but uses recursion`, function () { - // expect(sol.fillRecurse(3)).to.deep.equal([3, 3, 3]); - // }); - // }); - // describe("set(...args)", function () { - // it(`is given a list of arguments and returns an array with all duplicates - // removed`, function () { - // expect(sol.set(1, 1, 1, 2, 2, 2)).to.deep.equal([1, 2]); - // }); - // }); - // describe("identityf(x)", function () { - // it(`takes an argument and returns a function that returns that argument`, function () { - // assert.equal(sol.identityf(3)(), 3); - // }); - // }); - // describe("addf(a)", function () { - // it(`adds from two invocations`, function () { - // assert.equal(sol.addf(3)(4), 7); - // }); - // }); - // describe("liftf(binary)", function () { - // it(`takes a binary function, and makes it callable with two invocations`, function () { - // assert.equal(sol.liftf(sol.addb)(3)(4), 7); - // assert.equal(sol.liftf(sol.mulb)(5)(6), 30); - // }); - // }); - // describe("pure(x,y)", function () { - // it(`is a wrapper arround the impure function impure`, function () { - // expect(sol.pure(20, 5)).to.deep.equal([6, 120]); - // expect(sol.pure(25, 6)).to.deep.equal([7, 175]); - // }); - // }); + describe("fillRecurse(num)", function () { + + it(`does what fill does but uses recursion`, function () { + expect(sol.fillRecurse(3)).to.deep.equal([3, 3, 3]); + }); + + it('should throw a TypeError if second argument are not a number', () => { + expect(() => sol.fillRecurse()).to.throw(TypeError); + expect(() => sol.fillRecurse(sol.addb)).to.throw(TypeError); + expect(() => sol.fillRecurse('40')).to.throw(TypeError); + expect(() => sol.fillRecurse([])).to.throw(TypeError); + expect(() => sol.fillRecurse([])).to.throw(TypeError); + expect(() => sol.fillRecurse({})).to.throw(TypeError); + expect(() => sol.fillRecurse("foo")).to.throw(TypeError); + }); + + }); + describe("set(...args)", function () { + + it(`is given a list of arguments and returns an array with all duplicates + removed`, function () { + expect(sol.set(1, 1, 1, 2, 2, 2)).to.deep.equal([1, 2]); + }); + + it('should throw a TypeError if second argument are not a number', () => { + expect(() => sol.set(sol.addb)).to.throw(TypeError); + expect(() => sol.set('40')).to.throw(TypeError); + expect(() => sol.set([])).to.throw(TypeError); + expect(() => sol.set([], 12)).to.throw(TypeError); + expect(() => sol.set({})).to.throw(TypeError); + expect(() => sol.set('40', 2)).to.throw(TypeError); + expect(() => sol.set({}, 2)).to.throw(TypeError); + expect(() => sol.set("foo", "bar", 4)).to.throw(TypeError); + }); + + it('should throw a No arguments provided error if second argument are not a number', () => { + expect(() => sol.set()).to.throw('No arguments provided.'); + }); + + }); + + describe("identityf(x)", function () { + + it(`takes an argument and returns a function that returns that argument`, function () { + assert.equal(sol.identityf(3)(), 3); + }); + + }); + + + describe("addf(a)", function () { + + it(`adds from two invocations`, function () { + assert.equal(sol.addf(3)(4), 7); + }); + + it('should throw a TypeError if second argument are not a number', () => { + expect(() => sol.addf()).to.throw(TypeError); + expect(() => sol.addf(sol.addb)).to.throw(TypeError); + expect(() => sol.addf('40')).to.throw(TypeError); + expect(() => sol.addf([])).to.throw(TypeError); + expect(() => sol.addf([], 12)).to.throw(TypeError); + expect(() => sol.addf({})).to.throw(TypeError); + expect(() => sol.addf('40', 2)).to.throw(TypeError); + expect(() => sol.addf({}, 2)).to.throw(TypeError); + expect(() => sol.addf("foo", "bar", 4)).to.throw(TypeError); + }); + + }); + + describe("liftf(binary)", function () { + + it(`takes a binary function, and makes it callable with two invocations`, function () { + assert.equal(sol.liftf(sol.addb)(3)(4), 7); + assert.equal(sol.liftf(sol.mulb)(5)(6), 30); + }); + + it('should throw a TypeError if second argument are not a number', () => { + expect(() => sol.liftf()).to.throw(TypeError); + expect(() => sol.liftf(sol.liftf(sol.addb)("foo")(4))).to.throw(TypeError); + expect(() => sol.liftf(sol.liftf(sol.addb)([])(4))).to.throw(TypeError); + expect(() => sol.liftf(sol.liftf(sol.addb)({})(4))).to.throw(TypeError); + expect(() => sol.liftf(sol.liftf(sol.addb)()(4))).to.throw(TypeError); + expect(() => sol.liftf(sol.liftf(sol.addb)(42)([]))).to.throw(TypeError); + expect(() => sol.liftf(sol.liftf(sol.addb)(42)({}))).to.throw(TypeError); + expect(() => sol.liftf(sol.liftf(sol.addb)(42)())).to.throw(TypeError); + expect(() => sol.liftf(sol.liftf(sol.addb)(sol.mulb)(4))).to.throw(TypeError); + }); + + }); + + describe("pure(x,y)", function () { + + it(`is a wrapper arround the impure function impure`, function () { + expect(sol.pure(20, 5)).to.deep.equal([6, 120]); + expect(sol.pure(25, 6)).to.deep.equal([7, 175]); + }); + + }); + + // describe("curryb(binary, a)", function () { // it(`takes a binary function and an argument, and returns a function that can take // a second argument`, function () { From 80b705ed804a3c4224c4d08ec098d86663512998 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Fri, 16 Feb 2024 18:48:01 +0100 Subject: [PATCH 06/15] [ADD] new functionality (see description) - [ADD] func + test for: curryb, - [ADD] func + test for: curry, - [ADD] func + test for: inc, - [ADD] func + test for: twiceUnary, - [ADD] func + test for: doubl --- Solutions/Bltzz_solution.js | 82 +++++++++++++++++++++++++++++- test/Bltzz_tests.js | 99 +++++++++++++++++++++++++------------ 2 files changed, 148 insertions(+), 33 deletions(-) diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index 891e166..6ccd639 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -392,6 +392,81 @@ function pure(x, y) { return [y, z] } +/** + * curryb(binary, a) ⇒ function + * @param {binary} function - Any number + * @param {a} - Any number + * @returns {function} - returns a function that can take a second argument + * + * */ +function curryb(binary, a) { + _checkFunction(binary) + return function(b) { + return binary(a, b) + } +} + +/** + * curry(func, ...outer) ⇒ function + * @param {func} function - Any function + * @param {...outer} - Any list of args + * @returns {function} - returns a function that can take any list of args + * + * */ +function curry(func, ...outer) { + return function(...other) { + return func(...outer.concat(other)); + } +} + + + +/** + * inc(x) ⇒ number + * Without writting any new functions, show multiple ways to create the inc function + * + * Other options: + * 1. + * const inc = x => x + 1; + * 2. + * const inc = function(x) { + * return x + 1; + * }; + * + * @param {x} - Any number + * @returns {x + 1} - the stuff they asked for (x + 1). On nested calls: x + #of nests + * + * */ +function inc(x) { + return x + 1 +} + +/** + * twiceUnary(binary) ⇒ function + * + * @param {binary} - Any binary function + * @returns {function} - a unary function that passes its argument to the binary function twice + * + * */ +function twiceUnary(binary) { + return function(b){ + return binary(b, b) + } +} + +/** + * doubl(x) ⇒ number + * + * @param {x} - Any number + * @returns {function} - a unary function that passes its argument to the binary function twice + * + * */ +function doubl(x) { + _checkSingle(x); + return twiceUnary(add)(x) +} + + module.exports = { identity, addb, @@ -418,5 +493,10 @@ module.exports = { identityf, addf, liftf, - pure + pure, + curryb, + curry, + inc, + twiceUnary, + doubl } diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index 6f3079b..e2af0ad 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -512,38 +512,73 @@ describe("JS_Fun_Practice", function () { }); - // describe("curryb(binary, a)", function () { - // it(`takes a binary function and an argument, and returns a function that can take - // a second argument`, function () { - // assert.equal(sol.curryb(sol.addb, 3)(4), 7); - // assert.equal(sol.curryb(sol.mulb, 5)(6), 30); - // }); - // }); - // describe("curry(func, ...outer)", function () { - // it(`is a curry function generalized for any amount of arguments`, function () { - // assert.equal(sol.curry(sol.add, 1, 2, 4)(4, 2, 1), 14); - // assert.equal(sol.curry(sol.sub, 1, 2, 4)(4, 2, 1), -12); - // assert.equal(sol.curry(sol.mul, 1, 2, 4)(4, 2, 1), 64); - // }); - // }); - // describe("inc(x)", function () { - // it(`shows multiple ways to create the inc function`, function () { - // assert.equal(sol.inc(5), 6); - // assert.equal(sol.inc(sol.inc(5)), 7); - // }); - // }); - // describe("twiceUnary(binary)", function () { - // it(`takes a binary function and returns a unary function that passes its argument - // to the binary function twice`, function () { - // assert.equal(sol.twiceUnary(sol.addb)(11), 22); - // assert.equal(sol.twiceUnary(sol.mulb)(11), 121); - // }); - // }); - // describe("doubl(x)", function () { - // it(`uses the function twiceUnary to create the doubl function`, function () { - // assert.equal(sol.doubl(11), 22); - // }); - // }); + describe("curryb(binary, a)", function () { + + it(`takes a binary function and an argument, and returns a function that can take + a second argument`, function () { + assert.equal(sol.curryb(sol.addb, 3)(4), 7); + assert.equal(sol.curryb(sol.mulb, 5)(6), 30); + }); + + it('should throw a TypeError if second argument are not a number', () => { + expect(() => sol.curryb()).to.throw(TypeError); + expect(() => sol.curryb(sol.addb, 3)([])).to.throw(TypeError); + expect(() => sol.curryb(sol.addb, 3)({})).to.throw(TypeError); + expect(() => sol.curryb(sol.addb, 3)()).to.throw(TypeError); + expect(() => sol.curryb(sol.addb, 'foo')(4)).to.throw(TypeError); + expect(() => sol.curryb(sol.addb, [])(4)).to.throw(TypeError); + expect(() => sol.curryb(sol.addb, {})(4)).to.throw(TypeError); + expect(() => sol.curryb([], 3)(4)).to.throw(TypeError); + expect(() => sol.curryb({}, 3)(4)).to.throw(TypeError); + expect(() => sol.curryb('foo', 3)(4)).to.throw(TypeError); + expect(() => sol.curryb(3)(4)).to.throw(TypeError); + expect(() => sol.curryb([])(4)).to.throw(TypeError); + }); + + }); + + + describe("curry(func, ...outer)", function () { + + it(`is a curry function generalized for any amount of arguments`, function () { + assert.equal(sol.curry(sol.add, 1, 2, 4)(4, 2, 1), 14); + assert.equal(sol.curry(sol.sub, 1, 2, 4)(4, 2, 1), -12); + assert.equal(sol.curry(sol.mul, 1, 2, 4)(4, 2, 1), 64); + }); + + }); + + + describe("inc(x)", function () { + + it(`shows multiple ways to create the inc function`, function () { + assert.equal(sol.inc(5), 6); + assert.equal(sol.inc(sol.inc(5)), 7); + assert.equal(sol.inc(sol.inc(sol.inc(5))), 8); + }); + + }); + + + describe("twiceUnary(binary)", function () { + + it(`takes a binary function and returns a unary function that passes its argument + to the binary function twice`, function () { + assert.equal(sol.twiceUnary(sol.addb)(11), 22); + assert.equal(sol.twiceUnary(sol.mulb)(11), 121); + }); + + }); + + + describe("doubl(x)", function () { + + it(`uses the function twiceUnary to create the doubl function`, function () { + assert.equal(sol.doubl(11), 22); + }); + + }); + // describe("square(x)", function () { // it(`uses the function twiceUnary to create the square function`, function () { // assert.equal(sol.square(11), 121); From babdf800e5c90af76c89675f6f9a7121ca710ea4 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Mon, 19 Feb 2024 23:00:51 +0100 Subject: [PATCH 07/15] - [ADD] functions - see description - [ADD] func + test for: square, - [ADD] func + test for: twice, - [ADD] func + test for: reverseb, - [ADD] func + test for: reverse, - [ADD] func + test for: composeuTwo, - [ADD] func + test for: composeu, - [ADD] func + test for: composeb --- Solutions/Bltzz_solution.js | 104 +++++++++++++++++++++++++++++++++- test/Bltzz_tests.js | 108 +++++++++++++++++++++--------------- 2 files changed, 166 insertions(+), 46 deletions(-) diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index 6ccd639..c29fbc9 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -458,7 +458,7 @@ function twiceUnary(binary) { * doubl(x) ⇒ number * * @param {x} - Any number - * @returns {function} - a unary function that passes its argument to the binary function twice + * @returns {function} - Use the function twiceUnary to return the double of x * * */ function doubl(x) { @@ -466,6 +466,99 @@ function doubl(x) { return twiceUnary(add)(x) } +/** + * square(x) ⇒ number + * + * @param {x} - Any number + * @returns {function} - Use the function twiceUnary to return x square + * + * */ +function square(x) { + _checkSingle(x); + return twiceUnary(mul)(x) +} + +/** + * twice(x) ⇒ any + * + * @param {x} function - the add function + * @returns {function} - Use the function twiceUnary to return x square + * + * */ +function twice(x) { + return function(...args){ + return 2 * x(...args) + } +} + + +/** + * reverseb(binary) ⇒ function + * + * @param {binary} function - any binary function + * @returns {function} - Use the function twiceUnary to return x square + * + * */ +function reverseb(binary) { + return function(...args){ + return binary(...args.reverse()) + } +} + +/** + * reverse(func) ⇒ function + * + * @param {func} function - any function + * @returns {function} - Use the function twiceUnary to return x square + * + * */ +function reverse(binary) { + return function(...args){ + return reverseb(binary)(...args) + } +} + +/** + * composeuTwo(unary1, unary2) ⇒ function + * + * @param {unary1} function - any unary function + * @param {unary2} function - any unary function + * @returns {function} - Use the function twiceUnary to return x square + * + * */ +function composeuTwo(unary1, unary2) { + return function(arg){ + return unary2(unary1(arg)) + } +} + +/** + * composeu(...funcs) ⇒ any + * + * @param {...funcs} function - an array of functions + * @returns {function} - Use the function twiceUnary to return x square + * + * */ +function composeu(...funcs) { + return function(arg){ + return funcs.reduce((result, func) => func(result), arg); + } +} + + +/** + * composeb(binary1, binary2) ⇒ function + * + * @param {binary1} function - any unary function + * @param {binary2} function - any unary function + * @returns {function} - Use the function twiceUnary to return x square + * + * */ +function composeb(binary1, binary2) { + return function(...arg){ + return binary2(binary1(arg[0], arg[1]), arg[2]) + } +} module.exports = { identity, @@ -498,5 +591,12 @@ module.exports = { curry, inc, twiceUnary, - doubl + doubl, + square, + twice, + reverseb, + reverse, + composeuTwo, + composeu, + composeb } diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index e2af0ad..69cdf6c 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -579,50 +579,70 @@ describe("JS_Fun_Practice", function () { }); - // describe("square(x)", function () { - // it(`uses the function twiceUnary to create the square function`, function () { - // assert.equal(sol.square(11), 121); - // }); - // }); - // describe("twice(x)", function () { - // it(`is a twice function generalized for any amount of arguments`, function () { - // assert.equal(sol.twice(sol.add)(1, 2, 4), 14); - // }); - // }); - // describe("reverseb(binary)", function () { - // it(`reverses the arguments of a binary function`, function () { - // assert.equal(sol.reverseb(sol.subb)(3, 2), -1); - // }); - // }); - // describe("reverse(func)", function () { - // it(`is a reverse function generalized for any amount of arguments`, function () { - // assert.equal(sol.reverse(sol.sub)(1, 2, 4), 1); - // }); - // }); - // describe("composeuTwo(unary1,unary2)", function () { - // it(`takes two unary functions and returns a unary function that calls them - // both`, function () { - // assert.equal(sol.composeuTwo(sol.doubl, sol.square)(5), 100); - // }); - // }); - // describe("composeu(...funcs)", function () { - // it(`is a compose function generalized for any amount of arguments`, function () { - // assert.equal( - // sol.composeu( - // sol.doubl, - // sol.square, - // sol.identity, - // sol.curry(sol.add, 1, 2) - // )(5), - // 103 - // ); - // }); - // }); - // describe("composeb(binary1,binary2)", function () { - // it(`takes two binary functions and returns a function that calls them both`, function () { - // assert.equal(sol.composeb(sol.addb, sol.mulb)(2, 3, 7), 35); - // }); - // }); + describe("square(x)", function () { + + it(`uses the function twiceUnary to create the square function`, function () { + assert.equal(sol.square(11), 121); + }); + + }); + + describe("twice(x)", function () { + + it(`is a twice function generalized for any amount of arguments`, function () { + assert.equal(sol.twice(sol.add)(1, 2, 4), 14); + }); + + }); + + describe("reverseb(binary)", function () { + + it(`reverses the arguments of a binary function`, function () { + assert.equal(sol.reverseb(sol.subb)(3, 2), -1); + }); + + }); + + describe("reverse(func)", function () { + + it(`is a reverse function generalized for any amount of arguments`, function () { + assert.equal(sol.reverse(sol.sub)(1, 2, 4), 1); + }); + + }); + + describe("composeuTwo(unary1,unary2)", function () { + + it(`takes two unary functions and returns a unary function that calls them + both`, function () { + assert.equal(sol.composeuTwo(sol.doubl, sol.square)(5), 100); + }); + + }); + + describe("composeu(...funcs)", function () { + + it(`is a compose function generalized for any amount of arguments`, function () { + assert.equal( + sol.composeu( + sol.doubl, + sol.square, + sol.identity, + sol.curry(sol.add, 1, 2) + )(5), + 103 + ); + }); + + }); + + describe("composeb(binary1,binary2)", function () { + + it(`takes two binary functions and returns a function that calls them both`, function () { + assert.equal(sol.composeb(sol.addb, sol.mulb)(2, 3, 7), 35); + }); + + }); // describe("composeTwo(func1,func2)", function () { // it(`takes two functions and returns a function that calls them both`, function () { // assert.equal(sol.composeTwo(sol.add, sol.square)(2, 3, 7), 144); From d163a2a58e6a8ec97fae78d4096fc5bdb6834164 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Mon, 19 Feb 2024 23:27:27 +0100 Subject: [PATCH 08/15] [ADD] Lint for my code include eslint with airbnb code style to my js - [STYLE] The current state of the code --- .eslintrc.js | 18 ++ Solutions/Bltzz_solution.js | 523 ++++++++++++++++++------------------ package.json | 4 + test/Bltzz_tests.js | 320 +++++++++------------- 4 files changed, 399 insertions(+), 466 deletions(-) create mode 100644 .eslintrc.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..f33a9a7 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,18 @@ +module.exports = { + "extends": "airbnb-base", + "env": { + "node":true, + "mocha": true, + "es6": true, + }, + "globals": { + "expect": true, + "sinon": true, + }, + "plugins": [ + "import" + ], + "rules": { + "no-underscore-dangle": 0, + } + }; \ No newline at end of file diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index c29fbc9..34921a4 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -1,31 +1,26 @@ -/* +/* * @author Bltzz * */ function _check(x, y) { - if (typeof x !== 'number') - throw new TypeError(`${x} is not a number`); - if (typeof y !== 'number') - throw new TypeError(`${y} is not a number`); + if (typeof x !== 'number') { throw new TypeError(`${x} is not a number`); } + if (typeof y !== 'number') { throw new TypeError(`${y} is not a number`); } } function _checkSingle(x) { - if (typeof x !== 'number' || x === undefined) - throw new TypeError(`${x} is not a number`); + if (typeof x !== 'number' || x === undefined) { throw new TypeError(`${x} is not a number`); } } function _checkArray(...nums) { - if (nums.length === 0 || nums === undefined) { - throw new Error('No arguments provided.'); - } - nums.forEach(x => _checkSingle(x)) + if (nums.length === 0 || nums === undefined) { + throw new Error('No arguments provided.'); + } + nums.forEach(x => _checkSingle(x)); } -function _checkFunction(func){ - if (typeof func !== 'function') - throw new TypeError('Input must be a function'); - +function _checkFunction(func) { + if (typeof func !== 'function') { throw new TypeError('Input must be a function'); } } /** @@ -34,7 +29,7 @@ function _checkFunction(func){ * @returns {*} - The same value */ function identity(x) { - return x + return x; } /** @@ -44,8 +39,8 @@ function identity(x) { * @returns {*} - x plus y */ function addb(a, b) { - _check(a, b) - return a + b + _check(a, b); + return a + b; } /** @@ -55,8 +50,8 @@ function addb(a, b) { * @returns {*} - x minus y */ function subb(a, b) { - _check(a, b) - return a - b + _check(a, b); + return a - b; } /** @@ -66,8 +61,8 @@ function subb(a, b) { * @returns {*} - x times y */ function mulb(a, b) { - _check(a, b) - return a * b + _check(a, b); + return a * b; } /** @@ -77,8 +72,8 @@ function mulb(a, b) { * @returns {*} - The smaller value */ function minb(a, b) { - _check(a, b) - return a < b ? a : b + _check(a, b); + return a < b ? a : b; } /** @@ -88,8 +83,8 @@ function minb(a, b) { * @returns {*} - The bigger value */ function maxb(a, b) { - _check(a, b) - return a > b ? a : b + _check(a, b); + return a > b ? a : b; } /** @@ -98,28 +93,30 @@ function maxb(a, b) { * @returns {number} - The sum of all the numeric values */ function add(...nums) { - _checkArray(...nums) - let sum = 0; - for (let i = 0; i < nums.length; i++) sum += nums[i] - // Alternative: - // Use build-in reduce function - // return nums.reduce((sum, num) => sum + num, 0); - return sum + _checkArray(...nums); + // Use build-in reduce function + return nums.reduce((sum, num) => sum + num, 0); + + // Alternative: + // let sum = 0; + // for (let i = 0; i < nums.length; i++) sum += nums[i]; + // return sum; } /** * sub(...nums) ⇒ number * @param {...number} nums - Any number of numeric values - * @returns {number} - The result of subtracting all the numeric values - taking first element as starting point + * @returns {number} - The result of subtracting all the numeric values + * - taking first element as starting point */ function sub(...nums) { - _checkArray(...nums) - let result = nums[0]; - for (let i = 1; i < nums.length; i++) result -= nums[i]; - // Alternative: - // Use built-in reduce function - // return nums.reduce((result, num) => result - num); - return result; + _checkArray(...nums); + // let result = nums[0]; + // for (let i = 1; i < nums.length; i++) result -= nums[i]; + // Alternative: + // Use built-in reduce function + return nums.reduce((result, num) => result - num); + // return result; } /** @@ -128,13 +125,13 @@ function sub(...nums) { * @returns {number} - The result of mulitplying all the numeric values */ function mul(...nums) { - _checkArray(...nums) - let result = 1; - for (let i = 0; i < nums.length; i++) result *= nums[i]; - // Alternative: - // Use built-in reduce function - // return nums.reduce((result, num) => result * num); - return result; + _checkArray(...nums); + // let result = 1; + // for (let i = 0; i < nums.length; i++) result *= nums[i]; + // Alternative: + // Use built-in reduce function + return nums.reduce((result, num) => result * num); + // return result; } /** @@ -143,13 +140,13 @@ function mul(...nums) { * @returns {number} - The smallest of the numeric values */ function min(...nums) { - _checkArray(...nums) - let result = nums[0]; - for (let i = 0; i < nums.length; i++) (result > nums[i + 1]) ? result = nums[i + 1] : result - // Alternative: - // Use built-in Math.min function - //return Math.min(...nums); - return result; + _checkArray(...nums); + // let result = nums[0]; + // for (let i = 0; i < nums.length; i++) (result > nums[i + 1]) ? result = nums[i + 1] : result; + // Alternative: + // Use built-in Math.min function + return Math.min(...nums); + // return result; } /** @@ -158,13 +155,13 @@ function min(...nums) { * @returns {number} - The biggest of the numeric values */ function max(...nums) { - _checkArray(...nums) - let result = nums[0]; - for (let i = 0; i < nums.length; i++) (result < nums[i + 1]) ? result = nums[i + 1] : result - // Alternative: - // Use built-in Math.max function - //return Math.max(...nums); - return result; + _checkArray(...nums); + // let result = nums[0]; + // for (let i = 0; i < nums.length; i++) (result < nums[i + 1]) ? result = nums[i + 1] : result; + // Alternative: + // Use built-in Math.max function + return Math.max(...nums); + // return result; } /** @@ -173,10 +170,9 @@ function max(...nums) { * @returns {number} - The sum of the numeric values */ function addRecurse(...nums) { - nums.forEach(x => _checkSingle(x)) - if (nums.length === 0) - return 0; - return nums[0] + addRecurse(...nums.slice(1)); + nums.forEach(x => _checkSingle(x)); + if (nums.length === 0) { return 0; } + return nums[0] + addRecurse(...nums.slice(1)); } /** @@ -185,10 +181,9 @@ function addRecurse(...nums) { * @returns {number} - The product of the numeric values */ function mulRecurse(...nums) { - nums.forEach(x => _checkSingle(x)) - if (nums.length === 0) - return 1; - return nums[0] * mulRecurse(...nums.slice(1)); + nums.forEach(x => _checkSingle(x)); + if (nums.length === 0) { return 1; } + return nums[0] * mulRecurse(...nums.slice(1)); } /** @@ -197,12 +192,12 @@ function mulRecurse(...nums) { * @returns {number} - The smallest of the numeric values */ function minRecurse(...nums) { - nums.forEach(x => _checkSingle(x)) - // Base case: return -Infinity if no arguments are provided - if (nums.length === 0) return Infinity; - // Base case: return the single value if only one argument is provided - if (nums.length === 1) return nums[0]; - return nums[0] < minRecurse(...nums.slice(1)) ? nums[0] : minRecurse(...nums.slice(1)); + nums.forEach(x => _checkSingle(x)); + // Base case: return -Infinity if no arguments are provided + if (nums.length === 0) return Infinity; + // Base case: return the single value if only one argument is provided + if (nums.length === 1) return nums[0]; + return nums[0] < minRecurse(...nums.slice(1)) ? nums[0] : minRecurse(...nums.slice(1)); } /** @@ -211,13 +206,13 @@ function minRecurse(...nums) { * @returns {number} - The biggest of the numeric values */ function maxRecurse(...nums) { - nums.forEach(x => _checkSingle(x)) - // Base case: return -Infinity if no arguments are provided - if (nums.length === 0) return -Infinity; - // Base case: return the single value if only one argument is provided - if (nums.length === 1) return nums[0]; - // Recursive case: compare the first number with the maximum of the rest - return nums[0] > maxRecurse(...nums.slice(1)) ? nums[0] : maxRecurse(...nums.slice(1)); + nums.forEach(x => _checkSingle(x)); + // Base case: return -Infinity if no arguments are provided + if (nums.length === 0) return -Infinity; + // Base case: return the single value if only one argument is provided + if (nums.length === 1) return nums[0]; + // Recursive case: compare the first number with the maximum of the rest + return nums[0] > maxRecurse(...nums.slice(1)) ? nums[0] : maxRecurse(...nums.slice(1)); } /** @@ -225,44 +220,49 @@ function maxRecurse(...nums) { * @param {func} function - Any function * @returns {func} - The inverse res of the function */ -function not(func){ - _checkFunction(func) - return function (...args) { - return !func(...args); - }; +function not(func) { + _checkFunction(func); + return function inverseFunc(...args) { + return !func(...args); + }; } /** * acc(func, initial) ⇒ function * @param {func} function - Any function - * @returns {func} - returns a number that runs the initial function on each argument, accumulating the result + * @returns {func} - returns a number that runs the initial function + * on each argument, accumulating the result */ -function acc(func, initial){ - _checkFunction(func) - _checkSingle(initial) - return function (...args) { - let x = initial; - for (let i of args) { - x = func(x, i); - } - return x; - }; +function acc(func, initial) { + _checkFunction(func); + _checkSingle(initial); + return function accumFunc(...args) { + return args.reduce((accumulator, current) => func(accumulator, current), initial); + }; +// return function accumFunc(...args) { +// let x = initial; +// for (const i of args) { +// x = func(x, i); +// } +// return x; +// }; } /** * accPartial(func, start, end) ⇒ function * @param {func} function - Any function - * @returns {func} - returns a function that accumulates a subset of its arguments by applying the given function to - all elements between start and end incl. the stuff before start and after end + * @returns {func} - returns a function that accumulates a subset of its arguments + * by applying the given function to + * all elements between start and end incl. the stuff before start and after end */ -function accPartial(func, start, end){ - _checkFunction(func) - _checkArray(start, end) - return function (...args) { - const subset = args.slice(start, end); - // return args.slice(0, start) + func(...subset) + args.slice(end, args.length); - return [...args.slice(0, start), func(...subset), ...args.slice(end)]; - }; +function accPartial(func, start, end) { + _checkFunction(func); + _checkArray(start, end); + return function rangeAppliedFunc(...args) { + const subset = args.slice(start, end); + // return args.slice(0, start) + func(...subset) + args.slice(end, args.length); + return [...args.slice(0, start), func(...subset), ...args.slice(end)]; + }; } /** @@ -270,21 +270,21 @@ function accPartial(func, start, end){ * @param {func} function - Any function * @returns {func} - Same as acc, but recursively */ -function accRecurse(func, initial){ - _checkFunction(func) - _checkArray(initial) - return function (...args) { - if (args.length === 0) { - return initial; - } - const first = args[0]; - const rest = args.slice(1); - // same as - // const [first, ...rest] = args; - const result = func(initial, first); +function accRecurse(func, initial) { + _checkFunction(func); + _checkArray(initial); + return function rangeAppliedRecurseFunc(...args) { + if (args.length === 0) { + return initial; + } + const first = args[0]; + const rest = args.slice(1); + // same as + // const [first, ...rest] = args; + const result = func(initial, first); - return acc(func, result)(...rest); - }; + return acc(func, result)(...rest); + }; } /** @@ -293,8 +293,8 @@ function accRecurse(func, initial){ * @returns {Array} - An num long array filled with num */ function fill(num) { - _checkSingle(num) - return Array(num).fill(num) + _checkSingle(num); + return Array(num).fill(num); } /** @@ -303,18 +303,18 @@ function fill(num) { * @returns {Array} - An num long array filled with num */ function fillRecurse(num, result = []) { - _checkSingle(num) - // Break Condition: if num is 0, return the result array - if (num === 0) { - return result; - } - // Add the given number to the result array - // as we decrease the number to make recursion possible, - // add length of the result to always add the same number. - result.push(num + result.length); + _checkSingle(num); + // Break Condition: if num is 0, return the result array + if (num === 0) { + return result; + } + // Add the given number to the result array + // as we decrease the number to make recursion possible, + // add length of the result to always add the same number. + result.push(num + result.length); - // Call fillRecurse recursively with num - 1 - return fillRecurse(num - 1, result); + // Call fillRecurse recursively with num - 1 + return fillRecurse(num - 1, result); } /** @@ -323,18 +323,8 @@ function fillRecurse(num, result = []) { * @returns {Array} - An num long array filled with num */ function set(...args) { - _checkArray(...args) - return Array.from(new Set(args)) -} - - -/** - * identityf(x) ⇒ function - * @param {num} function - Any number - * @returns {Array} - An num long array filled with num - */ -function identityf(x) { - return function() {return x} + _checkArray(...args); + return Array.from(new Set(args)); } /** @@ -343,7 +333,7 @@ function identityf(x) { * @returns {function(x)} - A function that returns x */ function identityf(x) { - return function() {return x} + return function innerIdentityf() { return x; }; } /** @@ -352,11 +342,11 @@ function identityf(x) { * @returns {function} - A function that adds from two invocations */ function addf(a) { - _checkSingle(a); - return function(b) { - _checkSingle(b) - return a + b; - }; + _checkSingle(a); + return function innerAdd(b) { + _checkSingle(b); + return a + b; + }; } /** @@ -365,14 +355,14 @@ function addf(a) { * @returns {Array} - An num long array filled with num */ function liftf(binary) { - _checkFunction(binary) - return function(a){ - _checkSingle(a) - return function(b){ - _checkSingle(b) - return binary(a, b); - }; + _checkFunction(binary); + return function firstOperandFunc(a) { + _checkSingle(a); + return function secondOperandFunc(b) { + _checkSingle(b); + return binary(a, b); }; + }; } /** @@ -382,14 +372,14 @@ function liftf(binary) { * @returns {Array} - An num long array filled with num */ function pure(x, y) { - _check(x, y) - var z; - function impure(x) { - y++; - z = x * y; - } - impure(x) - return [y, z] + _check(x, y); + let z; + function impure(a) { + y += 1; // this generates a lint warning - but I cannot fix it as this was given! + z = a * y; + } + impure(x); + return [y, z]; } /** @@ -397,13 +387,13 @@ function pure(x, y) { * @param {binary} function - Any number * @param {a} - Any number * @returns {function} - returns a function that can take a second argument - * + * * */ function curryb(binary, a) { - _checkFunction(binary) - return function(b) { - return binary(a, b) - } + _checkFunction(binary); + return function innercurryb(b) { + return binary(a, b); + }; } /** @@ -411,192 +401,191 @@ function curryb(binary, a) { * @param {func} function - Any function * @param {...outer} - Any list of args * @returns {function} - returns a function that can take any list of args - * + * * */ function curry(func, ...outer) { - return function(...other) { - return func(...outer.concat(other)); - } + return function innerCurry(...other) { + return func(...outer.concat(other)); + }; } - /** * inc(x) ⇒ number * Without writting any new functions, show multiple ways to create the inc function - * + * * Other options: - * 1. + * 1. * const inc = x => x + 1; - * 2. + * 2. * const inc = function(x) { * return x + 1; * }; - * + * * @param {x} - Any number * @returns {x + 1} - the stuff they asked for (x + 1). On nested calls: x + #of nests - * + * * */ function inc(x) { - return x + 1 + return x + 1; } /** * twiceUnary(binary) ⇒ function - * + * * @param {binary} - Any binary function * @returns {function} - a unary function that passes its argument to the binary function twice - * + * * */ function twiceUnary(binary) { - return function(b){ - return binary(b, b) - } + return function innerUnaryFunc(b) { + return binary(b, b); + }; } /** * doubl(x) ⇒ number - * + * * @param {x} - Any number * @returns {function} - Use the function twiceUnary to return the double of x - * + * * */ function doubl(x) { - _checkSingle(x); - return twiceUnary(add)(x) + _checkSingle(x); + return twiceUnary(add)(x); } /** * square(x) ⇒ number - * + * * @param {x} - Any number * @returns {function} - Use the function twiceUnary to return x square - * + * * */ function square(x) { - _checkSingle(x); - return twiceUnary(mul)(x) + _checkSingle(x); + return twiceUnary(mul)(x); } /** * twice(x) ⇒ any - * + * * @param {x} function - the add function * @returns {function} - Use the function twiceUnary to return x square - * + * * */ function twice(x) { - return function(...args){ - return 2 * x(...args) - } + return function innerDuplicate(...args) { + return 2 * x(...args); + }; } /** * reverseb(binary) ⇒ function - * + * * @param {binary} function - any binary function * @returns {function} - Use the function twiceUnary to return x square - * + * * */ function reverseb(binary) { - return function(...args){ - return binary(...args.reverse()) - } + return function innerReverseB(...args) { + return binary(...args.reverse()); + }; } /** * reverse(func) ⇒ function - * + * * @param {func} function - any function * @returns {function} - Use the function twiceUnary to return x square - * + * * */ function reverse(binary) { - return function(...args){ - return reverseb(binary)(...args) - } + return function innerReverse(...args) { + return reverseb(binary)(...args); + }; } /** * composeuTwo(unary1, unary2) ⇒ function - * + * * @param {unary1} function - any unary function * @param {unary2} function - any unary function * @returns {function} - Use the function twiceUnary to return x square - * + * * */ function composeuTwo(unary1, unary2) { - return function(arg){ - return unary2(unary1(arg)) - } + return function composeArg(arg) { + return unary2(unary1(arg)); + }; } /** * composeu(...funcs) ⇒ any - * + * * @param {...funcs} function - an array of functions * @returns {function} - Use the function twiceUnary to return x square - * + * * */ function composeu(...funcs) { - return function(arg){ - return funcs.reduce((result, func) => func(result), arg); - } + return function composeArg(arg) { + return funcs.reduce((result, func) => func(result), arg); + }; } /** * composeb(binary1, binary2) ⇒ function - * + * * @param {binary1} function - any unary function * @param {binary2} function - any unary function * @returns {function} - Use the function twiceUnary to return x square - * + * * */ function composeb(binary1, binary2) { - return function(...arg){ - return binary2(binary1(arg[0], arg[1]), arg[2]) - } + return function composeBArgs(...arg) { + return binary2(binary1(arg[0], arg[1]), arg[2]); + }; } module.exports = { - identity, - addb, - subb, - mulb, - minb, - maxb, - add, - sub, - mul, - min, - max, - addRecurse, - mulRecurse, - minRecurse, - maxRecurse, - not, - acc, - accPartial, - accRecurse, - fill, - fillRecurse, - set, - identityf, - addf, - liftf, - pure, - curryb, - curry, - inc, - twiceUnary, - doubl, - square, - twice, - reverseb, - reverse, - composeuTwo, - composeu, - composeb -} + identity, + addb, + subb, + mulb, + minb, + maxb, + add, + sub, + mul, + min, + max, + addRecurse, + mulRecurse, + minRecurse, + maxRecurse, + not, + acc, + accPartial, + accRecurse, + fill, + fillRecurse, + set, + identityf, + addf, + liftf, + pure, + curryb, + curry, + inc, + twiceUnary, + doubl, + square, + twice, + reverseb, + reverse, + composeuTwo, + composeu, + composeb, +}; diff --git a/package.json b/package.json index 7c6a34d..3c7e876 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "description": "A testing utility for the fun javascript functions", "main": "index.js", "scripts": { + "lint": "eslint **/Bltzz_*.js", "test": "mocha || true" }, "repository": { @@ -18,6 +19,9 @@ "homepage": "https://github.com/hazamaswag/JS_Fun_Practice#readme", "devDependencies": { "chai": "^4.2.0", + "eslint": "^8.56.0", + "eslint-config-airbnb-base": "^11.2.0", + "eslint-plugin-import": "^2.8.0", "mocha": "^10.2.0", "mocha-sinon": "^2.1.2", "sinon": "^9.0.2" diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index 69cdf6c..c98403b 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -8,29 +8,22 @@ const expect = chai.expect; chai.use(sinonChai); -const filename = 'Bltzz_solution'; -const sol = require('../Solutions/' + filename); +const sol = require('../Solutions/Bltzz_solution'); - - -describe("JS_Fun_Practice", function () { - - describe("identity()", function () { - - it("takes an argument and returns that argument", function () { +describe('JS_Fun_Practice', () => { + describe('identity()', () => { + it('takes an argument and returns that argument', () => { assert.equal(sol.identity(3), 3); }); - }); - describe("addb(a,b)", function () { - - it("takes two numbers and returns their sum", function () { + describe('addb(a,b)', () => { + it('takes two numbers and returns their sum', () => { assert.equal(sol.addb(3, 4), 7); }); - + it('should throw a TypeError if arguments are not numbers', () => { expect(() => sol.addb(40, '2')).to.throw(TypeError); expect(() => sol.addb(40, [])).to.throw(TypeError); @@ -39,13 +32,11 @@ describe("JS_Fun_Practice", function () { expect(() => sol.addb([], 2)).to.throw(TypeError); expect(() => sol.addb({}, 2)).to.throw(TypeError); }); - }); - describe("subb(a,b)", function () { - - it("takes two numbers and returns their difference", function () { + describe('subb(a,b)', () => { + it('takes two numbers and returns their difference', () => { assert.equal(sol.subb(3, 4), -1); }); @@ -57,13 +48,11 @@ describe("JS_Fun_Practice", function () { expect(() => sol.subb([], 2)).to.throw(TypeError); expect(() => sol.subb({}, 2)).to.throw(TypeError); }); - }); - describe("mulb(a,b)", function () { - - it("takes two numbers and returns their product", function () { + describe('mulb(a,b)', () => { + it('takes two numbers and returns their product', () => { assert.equal(sol.mulb(3, 4), 12); }); @@ -74,15 +63,13 @@ describe("JS_Fun_Practice", function () { expect(() => sol.mulb('40', 2)).to.throw(TypeError); expect(() => sol.mulb([], 2)).to.throw(TypeError); expect(() => sol.mulb({}, 2)).to.throw(TypeError); - expect(() => sol.mulb("foo", "bar")).to.throw(TypeError); + expect(() => sol.mulb('foo', 'bar')).to.throw(TypeError); }); - }); - describe("minb(a,b)", function () { - - it("takes two numbers and returns the smaller one", function () { + describe('minb(a,b)', () => { + it('takes two numbers and returns the smaller one', () => { assert.equal(sol.minb(3, 4), 3); }); @@ -93,15 +80,13 @@ describe("JS_Fun_Practice", function () { expect(() => sol.minb('40', 2)).to.throw(TypeError); expect(() => sol.minb([], 2)).to.throw(TypeError); expect(() => sol.minb({}, 2)).to.throw(TypeError); - expect(() => sol.minb("foo", "bar")).to.throw(TypeError); + expect(() => sol.minb('foo', 'bar')).to.throw(TypeError); }); - }); - describe("maxb(a,b)", function () { - - it("takes two numbers and returns the larger one", function () { + describe('maxb(a,b)', () => { + it('takes two numbers and returns the larger one', () => { assert.equal(sol.maxb(3, 4), 4); }); @@ -112,15 +97,13 @@ describe("JS_Fun_Practice", function () { expect(() => sol.maxb('40', 2)).to.throw(TypeError); expect(() => sol.maxb([], 2)).to.throw(TypeError); expect(() => sol.maxb({}, 2)).to.throw(TypeError); - expect(() => sol.maxb("foo", "bar")).to.throw(TypeError); + expect(() => sol.maxb('foo', 'bar')).to.throw(TypeError); }); - }); - - - describe("add(...nums)", function () { - it("is an add fuction that is generalized for any amount of arguments", function () { + + describe('add(...nums)', () => { + it('is an add fuction that is generalized for any amount of arguments', () => { assert.equal(sol.add(1, 2, 4), 7); }); @@ -131,20 +114,18 @@ describe("JS_Fun_Practice", function () { expect(() => sol.add('40', 2)).to.throw(TypeError); expect(() => sol.add([], 42, 2)).to.throw(TypeError); expect(() => sol.add({}, 2)).to.throw(TypeError); - expect(() => sol.add("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.add('foo', 'bar', 4)).to.throw(TypeError); expect(() => sol.add(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); expect(() => sol.add()).to.throw('No arguments provided.'); }); - }); - describe("sub(...nums)", function () { - - it("is a sub fuction that is generalized for any amount of arguments", function () { + describe('sub(...nums)', () => { + it('is a sub fuction that is generalized for any amount of arguments', () => { assert.equal(sol.sub(1, 2, 4), -5); }); - + it('should throw a TypeError if arguments are not numbers', () => { expect(() => sol.sub(40, 41, '2')).to.throw(TypeError); expect(() => sol.sub(40, [], 12)).to.throw(TypeError); @@ -152,20 +133,18 @@ describe("JS_Fun_Practice", function () { expect(() => sol.sub('40', 2)).to.throw(TypeError); expect(() => sol.sub([], 42, 2)).to.throw(TypeError); expect(() => sol.sub({}, 2)).to.throw(TypeError); - expect(() => sol.sub("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.sub('foo', 'bar', 4)).to.throw(TypeError); expect(() => sol.sub(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); expect(() => sol.sub()).to.throw('No arguments provided.'); }); - }); - describe("mul(...nums)", function () { - - it("is a mul fuction that is generalized for any amount of arguments", function () { + describe('mul(...nums)', () => { + it('is a mul fuction that is generalized for any amount of arguments', () => { assert.equal(sol.mul(1, 2, 4), 8); }); - + it('should throw a TypeError if arguments are not numbers', () => { expect(() => sol.mul(40, 41, '2')).to.throw(TypeError); expect(() => sol.mul(40, [], 12)).to.throw(TypeError); @@ -173,20 +152,18 @@ describe("JS_Fun_Practice", function () { expect(() => sol.mul('40', 2)).to.throw(TypeError); expect(() => sol.mul([], 42, 2)).to.throw(TypeError); expect(() => sol.mul({}, 2)).to.throw(TypeError); - expect(() => sol.mul("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.mul('foo', 'bar', 4)).to.throw(TypeError); expect(() => sol.mul(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); expect(() => sol.mul()).to.throw('No arguments provided.'); }); - }); - describe("min(...nums)", function () { - - it("is a min fuction that is generalized for any amount of arguments", function () { + describe('min(...nums)', () => { + it('is a min fuction that is generalized for any amount of arguments', () => { assert.equal(sol.min(1, 2, 4), 1); }); - + it('should throw a TypeError if arguments are not numbers', () => { expect(() => sol.min(40, 41, '2')).to.throw(TypeError); expect(() => sol.min(40, [], 12)).to.throw(TypeError); @@ -194,20 +171,18 @@ describe("JS_Fun_Practice", function () { expect(() => sol.min('40', 2)).to.throw(TypeError); expect(() => sol.min([], 42, 2)).to.throw(TypeError); expect(() => sol.min({}, 2)).to.throw(TypeError); - expect(() => sol.min("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.min('foo', 'bar', 4)).to.throw(TypeError); expect(() => sol.min(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); expect(() => sol.min()).to.throw('No arguments provided.'); }); - }); - describe("max(...nums)", function () { - - it("is a max fuction that is generalized for any amount of arguments", function () { + describe('max(...nums)', () => { + it('is a max fuction that is generalized for any amount of arguments', () => { assert.equal(sol.max(1, 6, 4), 6); }); - + it('should throw a TypeError if arguments are not numbers', () => { expect(() => sol.max(40, 41, '2')).to.throw(TypeError); expect(() => sol.max(40, [], 12)).to.throw(TypeError); @@ -215,17 +190,15 @@ describe("JS_Fun_Practice", function () { expect(() => sol.max('40', 2)).to.throw(TypeError); expect(() => sol.max([], 42, 2)).to.throw(TypeError); expect(() => sol.max({}, 2)).to.throw(TypeError); - expect(() => sol.max("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.max('foo', 'bar', 4)).to.throw(TypeError); expect(() => sol.max(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); expect(() => sol.max()).to.throw('No arguments provided.'); }); - }); - describe("addRecurse(...nums)", function () { - - it("is an add fuction that is generalized but uses recursion", function () { + describe('addRecurse(...nums)', () => { + it('is an add fuction that is generalized but uses recursion', () => { assert.equal(sol.addRecurse(1, 2, 4), 7); assert.equal(sol.addRecurse(), 0); }); @@ -237,16 +210,14 @@ describe("JS_Fun_Practice", function () { expect(() => sol.addRecurse('40', 2)).to.throw(TypeError); expect(() => sol.addRecurse([], 42, 2)).to.throw(TypeError); expect(() => sol.addRecurse({}, 2)).to.throw(TypeError); - expect(() => sol.addRecurse("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.addRecurse('foo', 'bar', 4)).to.throw(TypeError); expect(() => sol.addRecurse(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); }); - }); - describe("mulRecurse(...nums)", function () { - - it("is a mul fuction that is generalized but uses recursion", function () { + describe('mulRecurse(...nums)', () => { + it('is a mul fuction that is generalized but uses recursion', () => { assert.equal(sol.mulRecurse(1, 2, 4), 8); }); @@ -257,16 +228,14 @@ describe("JS_Fun_Practice", function () { expect(() => sol.mulRecurse('40', 2)).to.throw(TypeError); expect(() => sol.mulRecurse([], 42, 2)).to.throw(TypeError); expect(() => sol.mulRecurse({}, 2)).to.throw(TypeError); - expect(() => sol.mulRecurse("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.mulRecurse('foo', 'bar', 4)).to.throw(TypeError); expect(() => sol.mulRecurse(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); }); - }); - describe("minRecurse(...nums)", function () { - + describe('minRecurse(...nums)', () => { // TODO: Nice addon: Count the function calls by wrapping recursive method in class function // See: https://stackoverflow.com/questions/51699584/how-to-spy-on-a-recursive-function-in-javascript/51699585#51699585 - it("is a min fuction that is generalized but uses recursion", function () { + it('is a min fuction that is generalized but uses recursion', () => { assert.equal(sol.minRecurse(1, 2, 4), 1); assert.equal(sol.minRecurse(3, 2, 1), 1); }); @@ -278,16 +247,14 @@ describe("JS_Fun_Practice", function () { expect(() => sol.minRecurse('40', 2)).to.throw(TypeError); expect(() => sol.minRecurse([], 42, 2)).to.throw(TypeError); expect(() => sol.minRecurse({}, 2)).to.throw(TypeError); - expect(() => sol.minRecurse("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.minRecurse('foo', 'bar', 4)).to.throw(TypeError); expect(() => sol.minRecurse(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); }); - }); - - describe("maxRecurse(...nums)", function () { - - it("is a max fuction that is generalized but uses recursion", function () { + + describe('maxRecurse(...nums)', () => { + it('is a max fuction that is generalized but uses recursion', () => { assert.equal(sol.maxRecurse(1, 2, 4), 4); }); @@ -298,17 +265,15 @@ describe("JS_Fun_Practice", function () { expect(() => sol.maxRecurse('40', 2)).to.throw(TypeError); expect(() => sol.maxRecurse([], 42, 2)).to.throw(TypeError); expect(() => sol.maxRecurse({}, 2)).to.throw(TypeError); - expect(() => sol.maxRecurse("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.maxRecurse('foo', 'bar', 4)).to.throw(TypeError); expect(() => sol.maxRecurse(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); }); - }); - describe("not(func)", function () { - - it("takes a function and returns the negation of its result", function () { - const isOdd = (x) => x % 2 === 1; + describe('not(func)', () => { + it('takes a function and returns the negation of its result', () => { + const isOdd = x => x % 2 === 1; const isEven = sol.not(isOdd); assert.equal(isEven(1), false); assert.equal(isEven(2), true); @@ -317,18 +282,16 @@ describe("JS_Fun_Practice", function () { it('should throw a TypeError if arguments are not a function', () => { expect(() => sol.not(40, 41, '2')).to.throw(TypeError); }); - }); - describe("acc(func,initial)", function () { - + describe('acc(func,initial)', () => { it(`takes a function and an initial value and returns a function - that runs the initial function on each argument, accumulating the result`, function () { - let add = sol.acc(sol.addb, 0); + that runs the initial function on each argument, accumulating the result`, () => { + const add = sol.acc(sol.addb, 0); assert.equal(add(1, 2, 4), 7); - let mul = sol.acc(sol.mulb, 1); + const mul = sol.acc(sol.mulb, 1); assert.equal(mul(1, 2, 4), 8); }); @@ -337,23 +300,21 @@ describe("JS_Fun_Practice", function () { }); it('should throw a TypeError if second argument are not a number', () => { - expect(() => sol.acc(sol.addb, "ds")).to.throw(TypeError); + expect(() => sol.acc(sol.addb, 'ds')).to.throw(TypeError); }); - }); - describe("accPartial(func,start,end)", function () { - + describe('accPartial(func,start,end)', () => { it(`takes in a function, a start index, and an end index, and returns a function that accumulates a subset of its arguments by applying the given function to - all elements between start and end`, function () { + all elements between start and end`, () => { const addSecondToThird = sol.accPartial(sol.add, 1, 3); expect(addSecondToThird(1, 2, 4, 8)).to.deep.equal([1, 6, 8]); const subSecondToThird = sol.accPartial(sol.sub, 1, 3); expect(subSecondToThird(1, 2, 4, 8)).to.deep.equal([1, -2, 8]); - + const subSecondToFourth = sol.accPartial(sol.sub, 1, 4); expect(subSecondToFourth(1, 2, 4, 6, 8)).to.deep.equal([1, -8, 8]); }); @@ -363,19 +324,17 @@ describe("JS_Fun_Practice", function () { }); it('should throw a TypeError if second argument are not a number', () => { - expect(() => sol.accPartial(sol.addb, "ds")).to.throw(TypeError); + expect(() => sol.accPartial(sol.addb, 'ds')).to.throw(TypeError); }); - }); - describe("accRecurse(func,initial)", function () { - - it(`does what acc does but uses recursion`, function () { - let add = sol.accRecurse(sol.addb, 0); + describe('accRecurse(func,initial)', () => { + it('does what acc does but uses recursion', () => { + const add = sol.accRecurse(sol.addb, 0); assert.equal(add(1, 2, 4), 7); - let mul = sol.accRecurse(sol.mulb, 1); + const mul = sol.accRecurse(sol.mulb, 1); assert.equal(mul(1, 2, 4), 8); }); @@ -384,15 +343,13 @@ describe("JS_Fun_Practice", function () { }); it('should throw a TypeError if second argument are not a number', () => { - expect(() => sol.accRecurse(sol.addb, "ds")).to.throw(TypeError); + expect(() => sol.accRecurse(sol.addb, 'ds')).to.throw(TypeError); }); - }); - describe("fill(num)", function () { - + describe('fill(num)', () => { it(`takes a number and returns an array with that many numbers equal to the given - number`, function () { + number`, () => { expect(sol.fill(3)).to.deep.equal([3, 3, 3]); }); @@ -405,15 +362,13 @@ describe("JS_Fun_Practice", function () { expect(() => sol.fill({})).to.throw(TypeError); expect(() => sol.fill('40', 2)).to.throw(TypeError); expect(() => sol.fill({}, 2)).to.throw(TypeError); - expect(() => sol.fill("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.fill('foo', 'bar', 4)).to.throw(TypeError); }); - }); - describe("fillRecurse(num)", function () { - - it(`does what fill does but uses recursion`, function () { + describe('fillRecurse(num)', () => { + it('does what fill does but uses recursion', () => { expect(sol.fillRecurse(3)).to.deep.equal([3, 3, 3]); }); @@ -424,14 +379,12 @@ describe("JS_Fun_Practice", function () { expect(() => sol.fillRecurse([])).to.throw(TypeError); expect(() => sol.fillRecurse([])).to.throw(TypeError); expect(() => sol.fillRecurse({})).to.throw(TypeError); - expect(() => sol.fillRecurse("foo")).to.throw(TypeError); + expect(() => sol.fillRecurse('foo')).to.throw(TypeError); }); - }); - describe("set(...args)", function () { - + describe('set(...args)', () => { it(`is given a list of arguments and returns an array with all duplicates - removed`, function () { + removed`, () => { expect(sol.set(1, 1, 1, 2, 2, 2)).to.deep.equal([1, 2]); }); @@ -443,27 +396,23 @@ describe("JS_Fun_Practice", function () { expect(() => sol.set({})).to.throw(TypeError); expect(() => sol.set('40', 2)).to.throw(TypeError); expect(() => sol.set({}, 2)).to.throw(TypeError); - expect(() => sol.set("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.set('foo', 'bar', 4)).to.throw(TypeError); }); it('should throw a No arguments provided error if second argument are not a number', () => { expect(() => sol.set()).to.throw('No arguments provided.'); }); - }); - describe("identityf(x)", function () { - - it(`takes an argument and returns a function that returns that argument`, function () { + describe('identityf(x)', () => { + it('takes an argument and returns a function that returns that argument', () => { assert.equal(sol.identityf(3)(), 3); }); - }); - describe("addf(a)", function () { - - it(`adds from two invocations`, function () { + describe('addf(a)', () => { + it('adds from two invocations', () => { assert.equal(sol.addf(3)(4), 7); }); @@ -476,21 +425,19 @@ describe("JS_Fun_Practice", function () { expect(() => sol.addf({})).to.throw(TypeError); expect(() => sol.addf('40', 2)).to.throw(TypeError); expect(() => sol.addf({}, 2)).to.throw(TypeError); - expect(() => sol.addf("foo", "bar", 4)).to.throw(TypeError); + expect(() => sol.addf('foo', 'bar', 4)).to.throw(TypeError); }); - }); - describe("liftf(binary)", function () { - - it(`takes a binary function, and makes it callable with two invocations`, function () { + describe('liftf(binary)', () => { + it('takes a binary function, and makes it callable with two invocations', () => { assert.equal(sol.liftf(sol.addb)(3)(4), 7); assert.equal(sol.liftf(sol.mulb)(5)(6), 30); }); it('should throw a TypeError if second argument are not a number', () => { expect(() => sol.liftf()).to.throw(TypeError); - expect(() => sol.liftf(sol.liftf(sol.addb)("foo")(4))).to.throw(TypeError); + expect(() => sol.liftf(sol.liftf(sol.addb)('foo')(4))).to.throw(TypeError); expect(() => sol.liftf(sol.liftf(sol.addb)([])(4))).to.throw(TypeError); expect(() => sol.liftf(sol.liftf(sol.addb)({})(4))).to.throw(TypeError); expect(() => sol.liftf(sol.liftf(sol.addb)()(4))).to.throw(TypeError); @@ -499,23 +446,19 @@ describe("JS_Fun_Practice", function () { expect(() => sol.liftf(sol.liftf(sol.addb)(42)())).to.throw(TypeError); expect(() => sol.liftf(sol.liftf(sol.addb)(sol.mulb)(4))).to.throw(TypeError); }); - }); - describe("pure(x,y)", function () { - - it(`is a wrapper arround the impure function impure`, function () { + describe('pure(x,y)', () => { + it('is a wrapper arround the impure function impure', () => { expect(sol.pure(20, 5)).to.deep.equal([6, 120]); expect(sol.pure(25, 6)).to.deep.equal([7, 175]); }); - }); - describe("curryb(binary, a)", function () { - + describe('curryb(binary, a)', () => { it(`takes a binary function and an argument, and returns a function that can take - a second argument`, function () { + a second argument`, () => { assert.equal(sol.curryb(sol.addb, 3)(4), 7); assert.equal(sol.curryb(sol.mulb, 5)(6), 30); }); @@ -534,114 +477,91 @@ describe("JS_Fun_Practice", function () { expect(() => sol.curryb(3)(4)).to.throw(TypeError); expect(() => sol.curryb([])(4)).to.throw(TypeError); }); - }); - describe("curry(func, ...outer)", function () { - - it(`is a curry function generalized for any amount of arguments`, function () { + describe('curry(func, ...outer)', () => { + it('is a curry function generalized for any amount of arguments', () => { assert.equal(sol.curry(sol.add, 1, 2, 4)(4, 2, 1), 14); assert.equal(sol.curry(sol.sub, 1, 2, 4)(4, 2, 1), -12); assert.equal(sol.curry(sol.mul, 1, 2, 4)(4, 2, 1), 64); }); - }); - describe("inc(x)", function () { - - it(`shows multiple ways to create the inc function`, function () { + describe('inc(x)', () => { + it('shows multiple ways to create the inc function', () => { assert.equal(sol.inc(5), 6); assert.equal(sol.inc(sol.inc(5)), 7); assert.equal(sol.inc(sol.inc(sol.inc(5))), 8); }); - }); - describe("twiceUnary(binary)", function () { - + describe('twiceUnary(binary)', () => { it(`takes a binary function and returns a unary function that passes its argument - to the binary function twice`, function () { + to the binary function twice`, () => { assert.equal(sol.twiceUnary(sol.addb)(11), 22); assert.equal(sol.twiceUnary(sol.mulb)(11), 121); }); - }); - - describe("doubl(x)", function () { - it(`uses the function twiceUnary to create the doubl function`, function () { + describe('doubl(x)', () => { + it('uses the function twiceUnary to create the doubl function', () => { assert.equal(sol.doubl(11), 22); }); - }); - describe("square(x)", function () { - - it(`uses the function twiceUnary to create the square function`, function () { + describe('square(x)', () => { + it('uses the function twiceUnary to create the square function', () => { assert.equal(sol.square(11), 121); }); - }); - - describe("twice(x)", function () { - - it(`is a twice function generalized for any amount of arguments`, function () { + + describe('twice(x)', () => { + it('is a twice function generalized for any amount of arguments', () => { assert.equal(sol.twice(sol.add)(1, 2, 4), 14); }); - }); - describe("reverseb(binary)", function () { - - it(`reverses the arguments of a binary function`, function () { + describe('reverseb(binary)', () => { + it('reverses the arguments of a binary function', () => { assert.equal(sol.reverseb(sol.subb)(3, 2), -1); }); - }); - - describe("reverse(func)", function () { - - it(`is a reverse function generalized for any amount of arguments`, function () { + + describe('reverse(func)', () => { + it('is a reverse function generalized for any amount of arguments', () => { assert.equal(sol.reverse(sol.sub)(1, 2, 4), 1); }); - }); - describe("composeuTwo(unary1,unary2)", function () { - + describe('composeuTwo(unary1,unary2)', () => { it(`takes two unary functions and returns a unary function that calls them - both`, function () { + both`, () => { assert.equal(sol.composeuTwo(sol.doubl, sol.square)(5), 100); }); - }); - - describe("composeu(...funcs)", function () { - - it(`is a compose function generalized for any amount of arguments`, function () { + + describe('composeu(...funcs)', () => { + it('is a compose function generalized for any amount of arguments', () => { assert.equal( sol.composeu( sol.doubl, sol.square, sol.identity, - sol.curry(sol.add, 1, 2) + sol.curry(sol.add, 1, 2), )(5), - 103 + 103, ); }); - }); - - describe("composeb(binary1,binary2)", function () { - - it(`takes two binary functions and returns a function that calls them both`, function () { + + describe('composeb(binary1,binary2)', () => { + it('takes two binary functions and returns a function that calls them both', () => { assert.equal(sol.composeb(sol.addb, sol.mulb)(2, 3, 7), 35); }); - }); // describe("composeTwo(func1,func2)", function () { // it(`takes two functions and returns a function that calls them both`, function () { @@ -977,7 +897,8 @@ describe("JS_Fun_Practice", function () { // this.sinon.stub(console, "log"); // }); - // it(`takes a unary function and returns a function that takes a callback and an argument`, function () { + // it(`takes a unary function and returns a function + // that takes a callback and an argument`, function () { // sol.continuizeu(Math.sqrt)(console.log, 81); // expect(console.log.calledOnce).to.be.true; // expect(console.log.calledWith(9)).to.be.true; @@ -988,7 +909,8 @@ describe("JS_Fun_Practice", function () { // this.sinon.stub(console, "log"); // }); - // it(`takes a function and returns a function that takes a callback and an argument`, function () { + // it(`takes a function and returns a function + // that takes a callback and an argument`, function () { // sol.continuize(sol.mul)(console.log, 81, 4, 2); // expect(console.log.calledOnce).to.be.true; // expect(console.log.calledWith(648)).to.be.true; From b734fa2c6127d38f0ce95a1fa779ef0f38dd2fb0 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Thu, 22 Feb 2024 21:54:14 +0100 Subject: [PATCH 09/15] [ADD] addtl functionality - [ADD] func + test for: composeTwo, - [ADD] func + test for: compose, - [ADD] func + test for: limitb, - [ADD] func + test for: limit, - [ADD] func + test for: genFrom, - [ADD] func + test for: genTo, - [ADD] func + test for: genFromTo, - [ADD] func + test for: elementGen, - [ADD] func + test for: element, - [ADD] func + test for: collect, --- Solutions/Bltzz_solution.js | 211 ++++++++++++++++++++++++++++++++++-- test/Bltzz_tests.js | 188 +++++++++++++++++--------------- 2 files changed, 300 insertions(+), 99 deletions(-) diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index 34921a4..9f23065 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -239,13 +239,13 @@ function acc(func, initial) { return function accumFunc(...args) { return args.reduce((accumulator, current) => func(accumulator, current), initial); }; -// return function accumFunc(...args) { -// let x = initial; -// for (const i of args) { -// x = func(x, i); -// } -// return x; -// }; + // return function accumFunc(...args) { + // let x = initial; + // for (const i of args) { + // x = func(x, i); + // } + // return x; + // }; } /** @@ -525,7 +525,7 @@ function composeuTwo(unary1, unary2) { * composeu(...funcs) ⇒ any * * @param {...funcs} function - an array of functions - * @returns {function} - Use the function twiceUnary to return x square + * @returns {function} - composes a generic amount of functions * * */ function composeu(...funcs) { @@ -538,9 +538,9 @@ function composeu(...funcs) { /** * composeb(binary1, binary2) ⇒ function * - * @param {binary1} function - any unary function - * @param {binary2} function - any unary function - * @returns {function} - Use the function twiceUnary to return x square + * @param {binary1} function - any binary function + * @param {binary2} function - any binary function + * @returns {function} - composes two functions to a single one that calls both (binary) * * */ function composeb(binary1, binary2) { @@ -549,6 +549,185 @@ function composeb(binary1, binary2) { }; } +/** + * composeTwo(func1, func2) ⇒ function + * + * @param {func1} function - any function + * @param {func2} function - any function + * @returns {function} - composes two functions to a single one that calls both + * (independend of arg length) + * + * */ +function composeTwo(func1, func2) { + return function composeBArgs(...arg) { + return func2(func1(...arg)); + }; +} + +/** + * compose(...funcs) ⇒ function + * + * @param {...funcs} function - any list of function + * @returns {function} - composes any amount functions to a single one that calls all of them + * (independend of arg length) + * + * */ +// max(fill(double(add(...args)))) +function compose(...funcs) { + // It returns a new function that will apply each function in the array from right to left + return function applyFromRightToLeft(...args) { + // This reduce function iterates over the functions array + return funcs.reduce((result, fn) => { + // If the result is not an array, convert it to an array + const argsArray = Array.isArray(result) ? result : [result]; + // Apply the current function to the arguments (result or [result]) + return fn(...argsArray); + }, args); // Initial arguments are passed to the first function + }; +} + +/** + * limitb(binary, lmt) ⇒ function + * + * @param {binary} function - any function + * @param {lmt} number - any limit (integer) + * @returns {function} - returns a function that can be calles lmt times + * + * */ +function limitb(binary, lmt) { + let callCount = 0; + return function limitedBinary(arg1, arg2) { + callCount += 1; // to be lint compliant + return (callCount <= lmt) ? binary(arg1, arg2) : undefined; + // one could also do this without the extra line for incrementing the counter + // return (++callCount <= lmt) ? binary(arg1, arg2) : undefined; + }; +} + +/** + * limit(func, lmt) ⇒ function + * + * @param {func} function - any function + * @param {lmt} number - any limit (integer) + * @returns {function} - returns a function that can be calles lmt times + * + * */ +function limit(func, lmt) { + let callCount = 0; + return function limitedBinary(...args) { + callCount += 1; // to be lint compliant + return (callCount <= lmt) ? func(...args) : undefined; + // one could also do this without the extra line for incrementing the counter + // return (++callCount <= lmt) ? func(...args) : undefined; + }; +} + +/** + * genFrom(x) ⇒ function + * + * @param {x} number - any integer + * @returns {number} - the count of how often it has been called + x + * + * */ +function* genFrom(x) { + let currentValue = x; + while (true) { + yield currentValue; + currentValue += 1; + } +} + +/** + * genTo(gen, lmt) ⇒ function + * + * @param {gen} function - the generator + * @param {lmt} number - any integer + * + * */ +function* genTo(gen, lmt) { + let lim = lmt - 1; + let next = gen.next(); + + while (!next.done && lim > 0) { + yield next.value; + lim -= 1; + next = gen.next(); + } +} + +/** + * genFromTo(start, end) ⇒ function + * + * @param {start} number - any integer + * @param {end} number - any integer + * + * */ +function* genFromTo(start, end) { + if (start >= end) throw Error('Start must not be greater than end'); + let currentValue = start; + while (currentValue < end) { + yield currentValue; + currentValue += 1; + } + return undefined; +} + +/** + * elementGen(array, gen) ⇒ function + * + * @param {array} array - any array + * @param {gen} function* - the generator func + * @returns {function} - returns a generator that will produce elements from the array + * + * */ +function* elementGen(array, gen) { + while (true) { + const index = gen.next().value; + // If the generator function reaches the end, break the loop + if (index === undefined) { + break; + } + yield array[index]; + } +} + +/** + * element(array, gen) ⇒ function + * + * @param {array} array - any array + * @param {gen} function* - the generator func + * @returns {function} - returns a generator that will produce elements from the array + * + * */ +function* element(array, gen) { + let currentValue = 0; + while (true) { + yield array[currentValue]; + currentValue += 1; + } +} + + +/** + * collect(gen, array) ⇒ function + * + * @param {array} array - any array + * @param {gen} function* - the generator func + * @returns {function} - returns a generator that will produce elements from the array + * + * */ +function* collect(gen, array) { + while (true) { + const index = gen.next().value; + // If the generator function reaches the end, break the loop + if (index === undefined) { + break; + } + array[index] = index; + yield array[index]; + } +} + module.exports = { identity, addb, @@ -588,4 +767,14 @@ module.exports = { composeuTwo, composeu, composeb, + composeTwo, + compose, + limitb, + limit, + genFrom, + genTo, + genFromTo, + elementGen, + element, + collect, }; diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index c98403b..453e199 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -232,6 +232,7 @@ describe('JS_Fun_Practice', () => { expect(() => sol.mulRecurse(4, 5, 6, 7, 8, 9, 10, 11, '12')).to.throw(TypeError); }); }); + describe('minRecurse(...nums)', () => { // TODO: Nice addon: Count the function calls by wrapping recursive method in class function // See: https://stackoverflow.com/questions/51699584/how-to-spy-on-a-recursive-function-in-javascript/51699585#51699585 @@ -563,94 +564,105 @@ describe('JS_Fun_Practice', () => { assert.equal(sol.composeb(sol.addb, sol.mulb)(2, 3, 7), 35); }); }); - // describe("composeTwo(func1,func2)", function () { - // it(`takes two functions and returns a function that calls them both`, function () { - // assert.equal(sol.composeTwo(sol.add, sol.square)(2, 3, 7), 144); - // }); - // }); - // describe("compose(...funcs)", function () { - // it(`takes any amount of functions and returns a function that takes any amount of - // arguments and gives them to the first function, then that result to the - // second function and so on`, function () { - // assert.equal( - // sol.compose(sol.add, sol.doubl, sol.fill, sol.max)(0, 1, 2), - // 6 - // ); - // }); - // }); - // describe("limitb(binary, lmt)", function () { - // it(`allows a binary function to be called a limited number of times`, function () { - // let addlmtb = sol.limitb(sol.addb, 1); - // assert.equal(addlmtb(3, 4), 7); - // assert.equal(addlmtb(3, 5), undefined); - // }); - // }); - // describe("limit(func, lmt)", function () { - // it(`allows a function that is generalized for any amount of arguments - // to be called a limited number of times`, function () { - // let addlmt = sol.limit(sol.add, 1); - // assert.equal(addlmt(1, 2, 4), 7); - // assert.equal(addlmt(3, 5, 9, 2), undefined); - // }); - // }); - // describe("genFrom(x)", function () { - // it(`produces a generator that will produces a series of values`, function () { - // let index = sol.genFrom(0); - // assert.equal(index.next().value, 0); - // assert.equal(index.next().value, 1); - // assert.equal(index.next().value, 2); - // }); - // }); - // describe("genTo(x)", function () { - // it(`takes a generator and an end limit, and returns a generator that will - // produce numbers up to that limit`, function () { - // let index = sol.genTo(sol.genFrom(1), 3); - // assert.equal(index.next().value, 1); - // assert.equal(index.next().value, 2); - // assert.equal(index.next().value, undefined); - // }); - // }); - // describe("genFromTo(x)", function () { - // it(`produces a generator that will produce values in a range`, function () { - // let index = sol.genFromTo(0, 3); - // assert.equal(index.next().value, 0); - // assert.equal(index.next().value, 1); - // assert.equal(index.next().value, 2); - // assert.equal(index.next().value, undefined); - // }); - // }); - // describe("elementGen(array,gen)", function () { - // it(`takes an array and a generator and returns a generator that will produce - // elements from the array`, function () { - // let ele = sol.elementGen(["a", "b", "c", "d"], sol.genFromTo(1, 3)); - // assert.equal(ele.next().value, "b"); - // assert.equal(ele.next().value, "c"); - // assert.equal(ele.next().value, undefined); - // }); - // }); - // describe("element(array,gen)", function () { - // it(`is a modified elementGen function so that the generator argument is optional. - // If a generator is not provided, then each of the elements of the array will - // be produced.`, function () { - // let ele = sol.element(["a", "b", "c", "d"]); - // assert.equal(ele.next().value, "a"); - // assert.equal(ele.next().value, "b"); - // assert.equal(ele.next().value, "c"); - // assert.equal(ele.next().value, "d"); - // assert.equal(ele.next().value, undefined); - // }); - // }); - // describe("collect(gen,array)", function () { - // it(`takes a generator and an array and produces a function that will collect the - // results in the array`, function () { - // let array = []; - // let col = sol.collect(sol.genFromTo(0, 2), array); - // assert.equal(col.next().value, 0); - // assert.equal(col.next().value, 1); - // assert.equal(col.next().value, undefined); - // expect(array).to.deep.equal([0, 1]); - // }); - // }); + + describe('composeTwo(func1,func2)', () => { + it('takes two functions and returns a function that calls them both', () => { + assert.equal(sol.composeTwo(sol.add, sol.square)(2, 3, 7), 144); + }); + }); + + describe('compose(...funcs)', () => { + it(`takes any amount of functions and returns a function that takes any amount of + arguments and gives them to the first function, then that result to the + second function and so on`, () => { + assert.equal( + sol.compose(sol.add, sol.doubl, sol.fill, sol.max)(0, 1, 2), 6); + }); + }); + + describe('limitb(binary, lmt)', () => { + it('allows a binary function to be called a limited number of times', () => { + const addlmtb = sol.limitb(sol.addb, 1); + assert.equal(addlmtb(3, 4), 7); + assert.equal(addlmtb(3, 5), undefined); + }); + }); + + describe('limit(func, lmt)', () => { + it(`allows a function that is generalized for any amount of arguments + to be called a limited number of times`, () => { + const addlmt = sol.limit(sol.add, 1); + assert.equal(addlmt(1, 2, 4), 7); + assert.equal(addlmt(3, 5, 9, 2), undefined); + }); + }); + + describe('genFrom(x)', () => { + it('produces a generator that will produces a series of values', () => { + const index = sol.genFrom(0); + assert.equal(index.next().value, 0); + assert.equal(index.next().value, 1); + assert.equal(index.next().value, 2); + }); + }); + + describe('genTo(x)', () => { + it(`takes a generator and an end limit, and returns a generator that will + produce numbers up to that limit`, () => { + const index = sol.genTo(sol.genFrom(1), 3); + assert.equal(index.next().value, 1); + assert.equal(index.next().value, 2); + assert.equal(index.next().value, undefined); + }); + }); + + describe('genFromTo(x)', () => { + it('produces a generator that will produce values in a range', () => { + const index = sol.genFromTo(0, 3); + assert.equal(index.next().value, 0); + assert.equal(index.next().value, 1); + assert.equal(index.next().value, 2); + assert.equal(index.next().value, undefined); + }); + it('throws and error if start is greater than end', () => { + const index = sol.genFromTo(3, 1); + expect(() => index.next().value).to.throw('Start must not be greater than end'); + }); + }); + + describe('elementGen(array,gen)', () => { + it(`takes an array and a generator and returns a generator that will produce + elements from the array`, () => { + const ele = sol.elementGen(['a', 'b', 'c', 'd'], sol.genFromTo(1, 3)); + assert.equal(ele.next().value, 'b'); + assert.equal(ele.next().value, 'c'); + assert.equal(ele.next().value, undefined); + }); + }); + + describe('element(array,gen)', () => { + it(`is a modified elementGen function so that the generator argument is optional. + If a generator is not provided, then each of the elements of the array will + be produced.`, () => { + const ele = sol.element(['a', 'b', 'c', 'd']); + assert.equal(ele.next().value, 'a'); + assert.equal(ele.next().value, 'b'); + assert.equal(ele.next().value, 'c'); + assert.equal(ele.next().value, 'd'); + assert.equal(ele.next().value, undefined); + }); + }); + describe('collect(gen,array)', () => { + it(`takes a generator and an array and produces a function that will collect the + results in the array`, () => { + const array = []; + const col = sol.collect(sol.genFromTo(0, 2), array); + assert.equal(col.next().value, 0); + assert.equal(col.next().value, 1); + assert.equal(col.next().value, undefined); + expect(array).to.deep.equal([0, 1]); + }); + }); // describe("filter(gen,predicate)", function () { // it(`takes a generator and a predicate and produces a generator that produces only // the values approved by the predicate`, function () { From 858d68f216625255f647a6985d0ca6a45302442d Mon Sep 17 00:00:00 2001 From: Bltzz Date: Thu, 22 Feb 2024 22:21:33 +0100 Subject: [PATCH 10/15] [ADD] more functions - [ADD] func + test for: filter, - [ADD] func + test for: filterTail, - [ADD] func + test for: concatTwo, - [ADD] func + test for: concat, --- Solutions/Bltzz_solution.js | 95 ++++++++++++++++++++++++++++++++++++- test/Bltzz_tests.js | 90 +++++++++++++++++------------------ 2 files changed, 138 insertions(+), 47 deletions(-) diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index 9f23065..bc59d9a 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -717,15 +717,102 @@ function* element(array, gen) { * * */ function* collect(gen, array) { + const arrCopy = array; while (true) { const index = gen.next().value; // If the generator function reaches the end, break the loop if (index === undefined) { break; } - array[index] = index; - yield array[index]; + arrCopy[index] = index; + yield arrCopy[index]; + } +} + +/** + * filter(gen, predicate) ⇒ function + * + * @param {gen} function - any generator function + * @param {predicate} function* - the predicate + * @returns {function} - produces a generator that produces only the values + * approved by the predicate + * + * */ +function* filter(gen, predicate) { + let result; + while (true) { + result = gen.next(); + if (result.done) break; + + const index = result.value; + if (predicate(index)) { + yield index; + } + } +} + +/** + * filterTail(gen, predicate) ⇒ function + * + * @param {gen} function - any generator function + * @param {predicate} function - the predicate + * @returns {function} - same as filter() but with tail-recursion to perform the filtering + * + * */ +function* filterTail(gen, predicate) { + const { done, value } = gen.next(); + if (done) { + return; + } + if (predicate(value)) { + yield value; } + yield* filterTail(gen, predicate); +} + +/** + * concatTwo(gen1, gen2) ⇒ function + * + * @param {gen1} function - any generator function + * @param {gen2} function* - any generator function + * @returns {function} - contatenated generators + * + * */ +function* concatTwo(gen1, gen2) { + let result; + while (true) { + result = gen1.next(); + if (result.done) break; + + yield result.value; + } + + while (true) { + result = gen2.next(); + if (result.done) break; + + yield result.value; + } +} + +/** + * concat(...gens) ⇒ function + * + * @param {...gens} function - any number of generator functions + * @returns {function} - contatenated generators + * + * */ +function* concat(...gens) { + const concatenated = gens.flatMap(gen => [...gen]); + yield* concatenated; +// for (const gen of gens) { +// let result; +// while (true) { +// result = gen.next(); +// if (result.done) break; +// yield result.value; +// } +// } } module.exports = { @@ -777,4 +864,8 @@ module.exports = { elementGen, element, collect, + filter, + filterTail, + concatTwo, + concat, }; diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index 453e199..1dcd113 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -663,51 +663,51 @@ describe('JS_Fun_Practice', () => { expect(array).to.deep.equal([0, 1]); }); }); - // describe("filter(gen,predicate)", function () { - // it(`takes a generator and a predicate and produces a generator that produces only - // the values approved by the predicate`, function () { - // let fil = sol.filter(sol.genFromTo(0, 5), (val) => val % 3 === 0); - // assert.equal(fil.next().value, 0); - // assert.equal(fil.next().value, 3); - // assert.equal(fil.next().value, undefined); - // }); - // }); - // describe("filterTail(gen,predicate)", function () { - // it(`uses tail-recursion to perform the filtering`, function () { - // let fil = sol.filterTail(sol.genFromTo(0, 5), (val) => val % 3 === 0); - // assert.equal(fil.next().value, 0); - // assert.equal(fil.next().value, 3); - // assert.equal(fil.next().value, undefined); - // }); - // }); - // describe("concatTwo(gen1,gen2)", function () { - // it(`takes two generators and produces a generator that combines the sequences`, function () { - // let con = sol.concatTwo(sol.genFromTo(0, 3), sol.genFromTo(0, 2)); - // assert.equal(con.next().value, 0); - // assert.equal(con.next().value, 1); - // assert.equal(con.next().value, 2); - // assert.equal(con.next().value, 0); - // assert.equal(con.next().value, 1); - // assert.equal(con.next().value, undefined); - // }); - // }); - // describe("concat(...gens)", function () { - // it(`is generalized for any amount of arguments`, function () { - // let con = sol.concat( - // sol.genFromTo(0, 3), - // sol.genFromTo(0, 2), - // sol.genFromTo(5, 7) - // ); - // assert.equal(con.next().value, 0); - // assert.equal(con.next().value, 1); - // assert.equal(con.next().value, 2); - // assert.equal(con.next().value, 0); - // assert.equal(con.next().value, 1); - // assert.equal(con.next().value, 5); - // assert.equal(con.next().value, 6); - // assert.equal(con.next().value, undefined); - // }); - // }); + describe('filter(gen,predicate)', () => { + it(`takes a generator and a predicate and produces a generator that produces only + the values approved by the predicate`, () => { + const fil = sol.filter(sol.genFromTo(0, 5), val => val % 3 === 0); + assert.equal(fil.next().value, 0); + assert.equal(fil.next().value, 3); + assert.equal(fil.next().value, undefined); + }); + }); + describe('filterTail(gen,predicate)', () => { + it('uses tail-recursion to perform the filtering', () => { + const fil = sol.filterTail(sol.genFromTo(0, 5), val => val % 3 === 0); + assert.equal(fil.next().value, 0); + assert.equal(fil.next().value, 3); + assert.equal(fil.next().value, undefined); + }); + }); + describe('concatTwo(gen1,gen2)', () => { + it('takes two generators and produces a generator that combines the sequences', () => { + const con = sol.concatTwo(sol.genFromTo(0, 3), sol.genFromTo(0, 2)); + assert.equal(con.next().value, 0); + assert.equal(con.next().value, 1); + assert.equal(con.next().value, 2); + assert.equal(con.next().value, 0); + assert.equal(con.next().value, 1); + assert.equal(con.next().value, undefined); + }); + }); + describe('concat(...gens)', () => { + it('is generalized for any amount of arguments', () => { + const con = sol.concat( + sol.genFromTo(0, 3), + sol.genFromTo(0, 2), + sol.genFromTo(5, 7), + ); + assert.equal(con.next().value, 0); + assert.equal(con.next().value, 1); + assert.equal(con.next().value, 2); + assert.equal(con.next().value, 0); + assert.equal(con.next().value, 1); + assert.equal(con.next().value, 5); + assert.equal(con.next().value, 6); + assert.equal(con.next().value, undefined); + }); + }); // describe("concatTail(...gens)", function () { // it(`uses tail-recursion to perform the concating`, function () { // let con = sol.concatTail( From 1dbb884e57859edd00741eb01329e04e4be14359 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Sun, 25 Feb 2024 16:01:07 +0100 Subject: [PATCH 11/15] [ADD] more functionality - [ADD] func + test for: concatTail, - [ADD] func + test for: gensymf, - [ADD] func + test for: gensymff, - [ADD] func + test for: fibonaccif, - [ADD] func + test for: counter, - [ADD] func + test for: revocableb, - [ADD] func + test for: revocable, - [ADD] func + test for: extract, - [ADD] func + test for: m, - [ADD] func + test for: addmTwo, - [ADD] func + test for: addm, - [ADD] func + test for: liftmbM, - [ADD] func + test for: liftmb, - [ADD] func + test for: liftm, - [ADD] func + test for: exp, - [ADD] func + test for: expn --- Solutions/Bltzz_solution.js | 320 +++++++++++++++++++++++++++++- test/Bltzz_tests.js | 379 ++++++++++++++++++++---------------- 2 files changed, 525 insertions(+), 174 deletions(-) diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index bc59d9a..5a51ef3 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -803,8 +803,8 @@ function* concatTwo(gen1, gen2) { * * */ function* concat(...gens) { - const concatenated = gens.flatMap(gen => [...gen]); - yield* concatenated; + const concatenated = gens.flatMap(gen => [...gen]); + yield* concatenated; // for (const gen of gens) { // let result; // while (true) { @@ -815,6 +815,306 @@ function* concat(...gens) { // } } +/** + * concatTail(...gens) ⇒ function + * + * @param {...gens} function - any list of generator functions + * @returns {function} - same as concat() but with tail-recursion to perform the filtering + * + * */ +function* concatTail(...gens) { + if (gens.length === 0) { + return; + } + + const [firstGen, ...remainingGens] = gens; + yield* firstGen; + yield* concatTail(...remainingGens); +} + +/** + * gensymf(symbol) ⇒ function + * + * @param {symbol} let - any character + * @returns {function} - a function that returns unique symbols + * + * */ +function* gensymf(symbol) { + const usedValues = []; + while (true) { + let cntr = 1; + while (usedValues.includes(symbol + cntr)) { + cntr += 1; + } + usedValues[cntr - 1] = symbol + cntr; + yield symbol + cntr; + } +} + +/** + * gensymff(unary, seed) ⇒ function + * + * @param {unary} function - any generator function + * @param {seed} - the seed + * @returns {function} - a function that returns unique symbols + * + * */ +function gensymff(unary, seed) { + return function* gensymfInner(symbol) { + let cntr = seed; + while (true) { + const result = unary(cntr); + cntr += 1; + yield symbol + result; + } + }; +} + +/** + * fibonaccif(first, second) ⇒ function + * + * @param {first} int - start val + * @param {second} int - next fibonacci element + * @returns {function} - the next fibonacci element + * + * */ +function* fibonaccif(first, second) { + let n1 = first; + let n2 = second; + let nextTerm = 0; + while (true) { + yield n1; + nextTerm = n1 + n2; + n1 = n2; + n2 = nextTerm; + } +} + +/** + * counter(i) ⇒ object + * + * @param {i} int - the start pos + * @returns {up} - function counts +1 + * @returns {down} - functions counts -1 + */ +function counter(i) { + let cntr = i; + return { + up() { + cntr += 1; + return cntr; + }, + down() { + cntr -= 1; + return cntr; + }, + }; +} + +/** + * revocableb(binary) ⇒ object + * + * @param {binary} function - any binary function + * @returns {invoke} - function invokes binary + * @returns {revoke} - function disables binary + */ +function revocableb(binary) { + let isAllowed = true; + return { + invoke(arg1, arg2) { + return isAllowed ? binary(arg1, arg2) : undefined; + }, + revoke() { + isAllowed = false; + }, + }; +} + +/** + * revocable(func) ⇒ object + * + * @param {func} function - any function + * @returns {invoke} - function invokes generic func + * @returns {revoke} - function disables function + */ +function revocable(func) { + let isAllowed = true; + return { + invoke(...args) { + return isAllowed ? func(...args) : undefined; + }, + revoke() { + isAllowed = false; + }, + }; +} + +/** + * extract(array, prop) ⇒ array + * + * @param {array} array - any array + * @param {prop} let - any peroperty name + * @returns {array} - an array with the extracted prop values + * + */ +function extract(array, prop) { + const property = prop; + const extractedValues = []; + array.forEach((obj) => { + if (obj[property] !== undefined) extractedValues.push(obj[property]); + }); + return extractedValues; +} + +/** + * m(value, source) ⇒ object + * + * @param {value} function - any function + * @param {source} let - optional: the source parameter + * @returns {obj} - an object + */ +function m(value, source) { + return { + value, + source: (source === undefined) ? value.toString() : source, + }; +} + +/** + * addmTwo(m1, m2) ⇒ object + * + * @param {m1} function - any m function + * @param {m2} function - any other m function + * @returns {obj} - an object + * + * */ +function addmTwo(m1, m2) { + m1Source = (m1.source === undefined) ? m1.value.toString() : m1.source; + m2Source = (m2.source === undefined) ? m2.value.toString() : m2.source; + return { + value: m1.value + m2.value, + source: "(" + m1Source + "+" + m2Source + ")" + }; +} + +/** + * addm(...ms) ⇒ object + * + * @param {m1} obj - any m function + * @param {m2} obj - any other m function + * @returns {obj} - an object + * + * */ +function addm(...ms) { + const value = ms.reduce((sum, current) => sum + current.value, 0); + const source = ms.map(current => (current.source === undefined ? current.value : current.source)).join("+"); + return { + value, + source: `(${source})` + }; +} + +/** + * liftmbM(binary, op) ⇒ object + * + * @param {binary} function - any function + * @param {m2} function - any other m function + * @returns {obj} - an object + * + * */ +function liftmbM(binary, op) { + return function(m1, m2){ + return { + value: binary(m1.value, m2.value), + source: "(" + (m1.source === undefined ? m1.value : m1.source) + op + (m2.source === undefined ? m2.value : m2.source) + ")" + } + } +} + +/** + * liftmb(binary, op) ⇒ object + * + * @param {binary} function - any binary function + * @param {op} let - the concatenator + * @returns {obj} - an object with the func applied + * + * */ +function liftmb(binary, op) { + return function(arg1, arg2){ + return { + value: binary(arg1, arg2), + source: "(" + arg1.toString() + op + arg2.toString() + ")" + } + } +} + +/** + * liftm(func, op) ⇒ object + * + * @param {func} function - any function + * @param {op} let - the concatenator + * @returns {obj} - an object with the func applied + * + * */ +function liftm(func, op) { + return function (...args) { + const initialValue = (func === addb) ? 0 : 1; + const processedArgs = args.map(arg => (typeof arg === 'object' ? arg.value : arg)); + const value = processedArgs.reduce((acc, current) => func(acc, current), initialValue); + const source = args.map(arg => (typeof arg === 'object' ? arg.source || arg.value : arg)).join(op); + + return { + value, + source: `(${source})` + }; + }; +} + +/** + * exp(value) ⇒ any + * + * @param {value} array - an array of any + * @returns the result of the operation + */ +function exp(value){ + if (typeof value[0] === 'function'){ + const result = value.slice(1).reduce((acc, num) => acc * num, 1); + return result; + } + return value; +} + +/** + * expn(value) ⇒ any + * + * @param {value} array - a nested array of any + * @returns the result of the operation + */ +function expn(value){ + if (typeof value === 'number') { + // If the element is a number, return it as is + return value; + } else if (Array.isArray(value)) { + // If the element is an array, recursively evaluate its contents + const operator = value[0]; + const operands = value.slice(1).map(expn); + + if (typeof operator === 'function') { + // If the first element is a function, apply it to the evaluated operands + return operator(...operands); + } else { + // If the first element is not a function, handle it accordingly + console.error("Invalid expression - the first element is not a function."); + return undefined; + } + } else { + // If the element is neither a number nor an array, handle it accordingly + console.error("Invalid expression - unexpected element type."); + return undefined; + } +} + + module.exports = { identity, addb, @@ -868,4 +1168,20 @@ module.exports = { filterTail, concatTwo, concat, + concatTail, + gensymf, + gensymff, + fibonaccif, + counter, + revocableb, + revocable, + extract, + m, + addmTwo, + addm, + liftmbM, + liftmb, + liftm, + exp, + expn }; diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index 1dcd113..6ec4279 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -652,6 +652,7 @@ describe('JS_Fun_Practice', () => { assert.equal(ele.next().value, undefined); }); }); + describe('collect(gen,array)', () => { it(`takes a generator and an array and produces a function that will collect the results in the array`, () => { @@ -663,6 +664,7 @@ describe('JS_Fun_Practice', () => { expect(array).to.deep.equal([0, 1]); }); }); + describe('filter(gen,predicate)', () => { it(`takes a generator and a predicate and produces a generator that produces only the values approved by the predicate`, () => { @@ -672,6 +674,7 @@ describe('JS_Fun_Practice', () => { assert.equal(fil.next().value, undefined); }); }); + describe('filterTail(gen,predicate)', () => { it('uses tail-recursion to perform the filtering', () => { const fil = sol.filterTail(sol.genFromTo(0, 5), val => val % 3 === 0); @@ -680,6 +683,7 @@ describe('JS_Fun_Practice', () => { assert.equal(fil.next().value, undefined); }); }); + describe('concatTwo(gen1,gen2)', () => { it('takes two generators and produces a generator that combines the sequences', () => { const con = sol.concatTwo(sol.genFromTo(0, 3), sol.genFromTo(0, 2)); @@ -691,6 +695,7 @@ describe('JS_Fun_Practice', () => { assert.equal(con.next().value, undefined); }); }); + describe('concat(...gens)', () => { it('is generalized for any amount of arguments', () => { const con = sol.concat( @@ -708,178 +713,208 @@ describe('JS_Fun_Practice', () => { assert.equal(con.next().value, undefined); }); }); - // describe("concatTail(...gens)", function () { - // it(`uses tail-recursion to perform the concating`, function () { - // let con = sol.concatTail( - // sol.genFromTo(0, 3), - // sol.genFromTo(0, 2), - // sol.genFromTo(5, 7) - // ); - // assert.equal(con.next().value, 0); - // assert.equal(con.next().value, 1); - // assert.equal(con.next().value, 2); - // assert.equal(con.next().value, 0); - // assert.equal(con.next().value, 1); - // assert.equal(con.next().value, 5); - // assert.equal(con.next().value, 6); - // assert.equal(con.next().value, undefined); - // }); - // }); - // describe("gensymf(symbol)", function () { - // it(`makes a function that generates unique symbols`, function () { - // let genG = sol.gensymf("G"); - // let genH = sol.gensymf("H"); - // assert.equal(genG.next().value, "G1"); - // assert.equal(genH.next().value, "H1"); - // assert.equal(genG.next().value, "G2"); - // assert.equal(genH.next().value, "H2"); - // }); - // }); - // describe("gensymff(unary, seed)", function () { - // it(`takes a unary function and a seed and returns a gensymf`, function () { - // let gensymf = sol.gensymff(sol.inc, 0); - // let genG = gensymf("G"); - // let genH = gensymf("H"); - // assert.equal(genG.next().value, "G1"); - // assert.equal(genH.next().value, "H1"); - // assert.equal(genG.next().value, "G2"); - // assert.equal(genH.next().value, "H2"); - // }); - // }); - // describe("fibonaccif(first, second)", function () { - // it(`returns a generator that will return the next fibonacci number`, function () { - // let fib = sol.fibonaccif(0, 1); - // assert.equal(fib.next().value, 0); - // assert.equal(fib.next().value, 1); - // assert.equal(fib.next().value, 1); - // assert.equal(fib.next().value, 2); - // assert.equal(fib.next().value, 3); - // assert.equal(fib.next().value, 5); - // assert.equal(fib.next().value, 8); - // }); - // }); - // describe("counter(i)", function () { - // it(`returns an object containing two functions that implement an up/down counter, - // hiding the counter`, function () { - // let obj = sol.counter(10); - // let { up, down } = obj; - // assert.equal(up(), 11); - // assert.equal(down(), 10); - // assert.equal(down(), 9); - // assert.equal(up(), 10); - // }); - // }); - // describe("revocableb(binary)", function () { - // it(`takes a binary function, and returns an object containing an invoke function - // that can invoke a function and a revoke function that disables the invoke - // function`, function () { - // let rev = sol.revocableb(sol.addb); - // assert.equal(rev.invoke(3, 4), 7); - // rev.revoke(); - // assert.equal(rev.invoke(5, 7), undefined); - // }); - // }); - // describe("revocable(binary)", function () { - // it(`takes a function that is generalized for any amount of arguments, and returns - // an object containing an invoke function that can invoke a function and a revoke - // function that disables the invoke function`, function () { - // let rev = sol.revocable(sol.add); - // assert.equal(rev.invoke(3, 4), 7); - // rev.revoke(); - // assert.equal(rev.invoke(5, 7), undefined); - // }); - // }); - // describe("extract(array,prop)", function () { - // it(`takes an array of objects and an object property name and converts each object - // in the array by extracting that property`, function () { - // let people = [{ name: "john" }, { name: "bob" }]; - // expect(sol.extract(people, "name")).to.deep.equal(["john", "bob"]); - // }); - // }); - // describe("m(value,source)", function () { - // it(`takes a value and an optional source string and returns them in an object`, function () { - // expect(sol.m(1)).to.deep.equal({ value: 1, source: "1" }); - // expect(sol.m(Math.PI, "pi")).to.deep.equal({ - // value: Math.PI, - // source: "pi", - // }); - // }); - // }); - // describe("addmTwo(m1,m2)", function () { - // it(`adds two m objects and returns an m object`, function () { - // expect(sol.addmTwo(sol.m(3), sol.m(4))).to.deep.equal({ - // value: 7, - // source: "(3+4)", - // }); - // expect(sol.addmTwo(sol.m(1), sol.m(Math.PI, "pi"))).to.deep.equal({ - // value: Math.PI + 1, - // source: "(1+pi)", - // }); - // }); - // }); - // describe("addm(...ms)", function () { - // it(`is a function that is generalized for any amount of arguments that adds m - // objects and returns an m object`, function () { - // expect(sol.addm(sol.m(1), sol.m(2), sol.m(4))).to.deep.equal({ - // value: 7, - // source: "(1+2+4)", - // }); - // }); - // }); - // describe("liftmbM(binary, op)", function () { - // it(`takes a binary function and a string and returns a function that acts on m - // objects`, function () { - // expect(sol.liftmbM(sol.addb, "+")(sol.m(3), sol.m(4))).to.deep.equal({ - // value: 7, - // source: "(3+4)", - // }); - // expect(sol.liftmbM(sol.mulb, "*")(sol.m(3), sol.m(4))).to.deep.equal({ - // value: 12, - // source: "(3*4)", - // }); - // }); - // }); - // describe("liftmb(binary, op)", function () { - // it(`is a modified function liftmbM that can accept arguments that are either numbers - // or m objects`, function () { - // expect(sol.liftmb(sol.addb, "+")(3, 4)).to.deep.equal({ - // value: 7, - // source: "(3+4)", - // }); - // }); - // }); - // describe("liftm(func, op)", function () { - // it(`is a modified function liftmbM that is generalized for any amount of arguments - // that can accept arguments that are either numbers or m objects`, function () { - // expect(sol.liftm(sol.addb, "+")(sol.m(3), sol.m(4))).to.deep.equal({ - // value: 7, - // source: "(3+4)", - // }); - // expect(sol.liftm(sol.mulb, "*")(sol.m(3), sol.m(4))).to.deep.equal({ - // value: 12, - // source: "(3*4)", - // }); - // expect(sol.liftm(sol.mulb, "*")(3, 4)).to.deep.equal({ - // value: 12, - // source: "(3*4)", - // }); - // }); - // }); - // describe("exp(value)", function () { - // it(`evaluates simple array expressions`, function () { - // assert.equal(sol.exp([sol.mul, 1, 2, 4]), 8); - // assert.equal(sol.exp(42), 42); - // }); - // }); - // describe("expn(value)", function () { - // it(`is a modified exp that can evaluate nested array expressions`, function () { - // assert.equal( - // sol.expn([Math.sqrt, [sol.add, [sol.square, 3], [sol.square, 4]]]), - // 5 - // ); - // // assert.equal(sol.expn(34), 34); - // }); - // }); + + describe('concatTail(...gens)', () => { + it('uses tail-recursion to perform the concating', () => { + const con = sol.concatTail( + sol.genFromTo(0, 3), + sol.genFromTo(0, 2), + sol.genFromTo(5, 7), + ); + assert.equal(con.next().value, 0); + assert.equal(con.next().value, 1); + assert.equal(con.next().value, 2); + assert.equal(con.next().value, 0); + assert.equal(con.next().value, 1); + assert.equal(con.next().value, 5); + assert.equal(con.next().value, 6); + assert.equal(con.next().value, undefined); + }); + }); + + describe('gensymf(symbol)', () => { + it('makes a function that generates unique symbols', () => { + const genG = sol.gensymf('G'); + const genH = sol.gensymf('H'); + assert.equal(genG.next().value, 'G1'); + assert.equal(genH.next().value, 'H1'); + assert.equal(genG.next().value, 'G2'); + assert.equal(genH.next().value, 'H2'); + assert.equal(genG.next().value, 'G3'); + assert.equal(genH.next().value, 'H3'); + assert.equal(genG.next().value, 'G4'); + assert.equal(genH.next().value, 'H4'); + assert.equal(genG.next().value, 'G5'); + assert.equal(genH.next().value, 'H5'); + assert.equal(genG.next().value, 'G6'); + assert.equal(genH.next().value, 'H6'); + }); + }); + + describe('gensymff(unary, seed)', () => { + it('takes a unary function and a seed and returns a gensymf', () => { + const gensymf = sol.gensymff(sol.inc, 0); + const genG = gensymf('G'); + const genH = gensymf('H'); + assert.equal(genG.next().value, 'G1'); + assert.equal(genH.next().value, 'H1'); + assert.equal(genG.next().value, 'G2'); + assert.equal(genH.next().value, 'H2'); + }); + }); + + describe('fibonaccif(first, second)', () => { + it('returns a generator that will return the next fibonacci number', () => { + const fib = sol.fibonaccif(0, 1); + assert.equal(fib.next().value, 0); + assert.equal(fib.next().value, 1); + assert.equal(fib.next().value, 1); + assert.equal(fib.next().value, 2); + assert.equal(fib.next().value, 3); + assert.equal(fib.next().value, 5); + assert.equal(fib.next().value, 8); + }); + it('returns a generator that will return the next fibonacci number with different start val', () => { + const fib = sol.fibonaccif(1, 2); + assert.equal(fib.next().value, 1); + assert.equal(fib.next().value, 2); + assert.equal(fib.next().value, 3); + assert.equal(fib.next().value, 5); + assert.equal(fib.next().value, 8); + }); + }); + + describe('counter(i)', () => { + it(`returns an object containing two functions that implement an up/down counter, + hiding the counter`, () => { + const obj = sol.counter(10); + const { up, down } = obj; + assert.equal(up(), 11); + assert.equal(down(), 10); + assert.equal(down(), 9); + assert.equal(up(), 10); + }); + }); + + describe('revocableb(binary)', () => { + it(`takes a binary function, and returns an object containing an invoke function + that can invoke a function and a revoke function that disables the invoke + function`, () => { + const rev = sol.revocableb(sol.addb); + assert.equal(rev.invoke(3, 4), 7); + rev.revoke(); + assert.equal(rev.invoke(5, 7), undefined); + }); + }); + + describe('revocable(binary)', () => { + it(`takes a function that is generalized for any amount of arguments, and returns + an object containing an invoke function that can invoke a function and a revoke + function that disables the invoke function`, () => { + const rev = sol.revocable(sol.add); + assert.equal(rev.invoke(3, 4), 7); + rev.revoke(); + assert.equal(rev.invoke(5, 7), undefined); + }); + }); + + describe('extract(array,prop)', () => { + it(`takes an array of objects and an object property name and converts each object + in the array by extracting that property`, () => { + const people = [{ name: 'john' }, { name: 'bob' }]; + expect(sol.extract(people, 'name')).to.deep.equal(['john', 'bob']); + }); + }); + describe('m(value,source)', () => { + it('takes a value and an optional source string and returns them in an object', () => { + expect(sol.m(1)).to.deep.equal({ value: 1, source: '1' }); + expect(sol.m(Math.PI, 'pi')).to.deep.equal({ + value: Math.PI, + source: 'pi', + }); + }); + }); + describe("addmTwo(m1,m2)", function () { + it(`adds two m objects and returns an m object`, function () { + expect(sol.addmTwo(sol.m(3), sol.m(4))).to.deep.equal({ + value: 7, + source: "(3+4)", + }); + expect(sol.addmTwo(sol.m(1), sol.m(Math.PI, "pi"))).to.deep.equal({ + value: Math.PI + 1, + source: "(1+pi)", + }); + }); + }); + + describe("addm(...ms)", function () { + it(`is a function that is generalized for any amount of arguments that adds m + objects and returns an m object`, function () { + expect(sol.addm(sol.m(1), sol.m(2), sol.m(4))).to.deep.equal({ + value: 7, + source: "(1+2+4)", + }); + }); + }); + + describe("liftmbM(binary, op)", function () { + it(`takes a binary function and a string and returns a function that acts on m + objects`, function () { + expect(sol.liftmbM(sol.addb, "+")(sol.m(3), sol.m(4))).to.deep.equal({ + value: 7, + source: "(3+4)", + }); + expect(sol.liftmbM(sol.mulb, "*")(sol.m(3), sol.m(4))).to.deep.equal({ + value: 12, + source: "(3*4)", + }); + }); + }); + + describe("liftmb(binary, op)", function () { + it(`is a modified function liftmbM that can accept arguments that are either numbers + or m objects`, function () { + expect(sol.liftmb(sol.addb, "+")(3, 4)).to.deep.equal({ + value: 7, + source: "(3+4)", + }); + }); + }); + + describe("liftm(func, op)", function () { + it(`is a modified function liftmbM that is generalized for any amount of arguments + that can accept arguments that are either numbers or m objects`, function () { + expect(sol.liftm(sol.addb, "+")(sol.m(3), sol.m(4))).to.deep.equal({ + value: 7, + source: "(3+4)", + }); + expect(sol.liftm(sol.mulb, "*")(sol.m(3), sol.m(4))).to.deep.equal({ + value: 12, + source: "(3*4)", + }); + expect(sol.liftm(sol.mulb, "*")(3, 4)).to.deep.equal({ + value: 12, + source: "(3*4)", + }); + }); + }); + + describe("exp(value)", function () { + it(`evaluates simple array expressions`, function () { + assert.equal(sol.exp([sol.mul, 1, 2, 4]), 8); + assert.equal(sol.exp(42), 42); + }); + }); + + describe("expn(value)", function () { + it(`is a modified exp that can evaluate nested array expressions`, function () { + assert.equal( + sol.expn([Math.sqrt, [sol.add, [sol.square, 3], [sol.square, 4]]]), + 5 + ); + // assert.equal(sol.expn(34), 34); + }); + }); // describe("addg(value)", function () { // it(`adds from many invocations, until it sees an empty invocation`, function () { // assert.equal(sol.addg(), undefined); From dfad601eda7faf114fb00056dea231a5206fe4cd Mon Sep 17 00:00:00 2001 From: Bltzz Date: Sun, 25 Feb 2024 16:04:33 +0100 Subject: [PATCH 12/15] [FIX] lint --- Solutions/Bltzz_solution.js | 84 ++++++++++++++++++------------------- test/Bltzz_tests.js | 64 ++++++++++++++-------------- 2 files changed, 73 insertions(+), 75 deletions(-) diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index 5a51ef3..2b43b4f 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -971,7 +971,7 @@ function extract(array, prop) { * * @param {value} function - any function * @param {source} let - optional: the source parameter - * @returns {obj} - an object + * @returns {obj} - an object */ function m(value, source) { return { @@ -985,15 +985,15 @@ function m(value, source) { * * @param {m1} function - any m function * @param {m2} function - any other m function - * @returns {obj} - an object - * + * @returns {obj} - an object + * * */ function addmTwo(m1, m2) { - m1Source = (m1.source === undefined) ? m1.value.toString() : m1.source; - m2Source = (m2.source === undefined) ? m2.value.toString() : m2.source; + const m1Source = (m1.source === undefined) ? m1.value.toString() : m1.source; + const m2Source = (m2.source === undefined) ? m2.value.toString() : m2.source; return { value: m1.value + m2.value, - source: "(" + m1Source + "+" + m2Source + ")" + source: `(${m1Source}+${m2Source})`, }; } @@ -1002,15 +1002,15 @@ function addmTwo(m1, m2) { * * @param {m1} obj - any m function * @param {m2} obj - any other m function - * @returns {obj} - an object - * + * @returns {obj} - an object + * * */ function addm(...ms) { const value = ms.reduce((sum, current) => sum + current.value, 0); - const source = ms.map(current => (current.source === undefined ? current.value : current.source)).join("+"); + const source = ms.map(current => (current.source === undefined ? current.value : current.source)).join('+'); return { value, - source: `(${source})` + source: `(${source})`, }; } @@ -1019,16 +1019,16 @@ function addm(...ms) { * * @param {binary} function - any function * @param {m2} function - any other m function - * @returns {obj} - an object - * + * @returns {obj} - an object + * * */ function liftmbM(binary, op) { - return function(m1, m2){ + return function innerCall(m1, m2) { return { value: binary(m1.value, m2.value), - source: "(" + (m1.source === undefined ? m1.value : m1.source) + op + (m2.source === undefined ? m2.value : m2.source) + ")" - } - } + source: `(${m1.source === undefined ? m1.value : m1.source}${op}${m2.source === undefined ? m2.value : m2.source})`, + }; + }; } /** @@ -1037,15 +1037,15 @@ function liftmbM(binary, op) { * @param {binary} function - any binary function * @param {op} let - the concatenator * @returns {obj} - an object with the func applied - * + * * */ function liftmb(binary, op) { - return function(arg1, arg2){ + return function innerCall(arg1, arg2) { return { value: binary(arg1, arg2), - source: "(" + arg1.toString() + op + arg2.toString() + ")" - } - } + source: `(${arg1.toString()}${op}${arg2.toString()})`, + }; + }; } /** @@ -1054,31 +1054,31 @@ function liftmb(binary, op) { * @param {func} function - any function * @param {op} let - the concatenator * @returns {obj} - an object with the func applied - * + * * */ function liftm(func, op) { - return function (...args) { + return function innerCall(...args) { const initialValue = (func === addb) ? 0 : 1; const processedArgs = args.map(arg => (typeof arg === 'object' ? arg.value : arg)); - const value = processedArgs.reduce((acc, current) => func(acc, current), initialValue); + const value = processedArgs.reduce((arg1, arg2) => func(arg1, arg2), initialValue); const source = args.map(arg => (typeof arg === 'object' ? arg.source || arg.value : arg)).join(op); return { value, - source: `(${source})` + source: `(${source})`, }; }; } /** * exp(value) ⇒ any - * + * * @param {value} array - an array of any * @returns the result of the operation */ -function exp(value){ - if (typeof value[0] === 'function'){ - const result = value.slice(1).reduce((acc, num) => acc * num, 1); +function exp(value) { + if (typeof value[0] === 'function') { + const result = value.slice(1).reduce((arg1, arg2) => arg1 * arg2, 1); return result; } return value; @@ -1086,11 +1086,11 @@ function exp(value){ /** * expn(value) ⇒ any - * + * * @param {value} array - a nested array of any * @returns the result of the operation */ -function expn(value){ +function expn(value) { if (typeof value === 'number') { // If the element is a number, return it as is return value; @@ -1102,16 +1102,14 @@ function expn(value){ if (typeof operator === 'function') { // If the first element is a function, apply it to the evaluated operands return operator(...operands); - } else { - // If the first element is not a function, handle it accordingly - console.error("Invalid expression - the first element is not a function."); - return undefined; } - } else { - // If the element is neither a number nor an array, handle it accordingly - console.error("Invalid expression - unexpected element type."); + // If the first element is not a function, handle it accordingly + // console.error('Invalid expression - the first element is not a function.'); return undefined; } + // If the element is neither a number nor an array, handle it accordingly + // console.error('Invalid expression - unexpected element type.'); + return undefined; } @@ -1178,10 +1176,10 @@ module.exports = { extract, m, addmTwo, - addm, + addm, liftmbM, - liftmb, - liftm, - exp, - expn + liftmb, + liftm, + exp, + expn, }; diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index 6ec4279..334f71f 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -834,83 +834,83 @@ describe('JS_Fun_Practice', () => { }); }); }); - describe("addmTwo(m1,m2)", function () { - it(`adds two m objects and returns an m object`, function () { + describe('addmTwo(m1,m2)', () => { + it('adds two m objects and returns an m object', () => { expect(sol.addmTwo(sol.m(3), sol.m(4))).to.deep.equal({ value: 7, - source: "(3+4)", + source: '(3+4)', }); - expect(sol.addmTwo(sol.m(1), sol.m(Math.PI, "pi"))).to.deep.equal({ + expect(sol.addmTwo(sol.m(1), sol.m(Math.PI, 'pi'))).to.deep.equal({ value: Math.PI + 1, - source: "(1+pi)", + source: '(1+pi)', }); }); }); - describe("addm(...ms)", function () { + describe('addm(...ms)', () => { it(`is a function that is generalized for any amount of arguments that adds m - objects and returns an m object`, function () { + objects and returns an m object`, () => { expect(sol.addm(sol.m(1), sol.m(2), sol.m(4))).to.deep.equal({ value: 7, - source: "(1+2+4)", + source: '(1+2+4)', }); }); }); - describe("liftmbM(binary, op)", function () { + describe('liftmbM(binary, op)', () => { it(`takes a binary function and a string and returns a function that acts on m - objects`, function () { - expect(sol.liftmbM(sol.addb, "+")(sol.m(3), sol.m(4))).to.deep.equal({ + objects`, () => { + expect(sol.liftmbM(sol.addb, '+')(sol.m(3), sol.m(4))).to.deep.equal({ value: 7, - source: "(3+4)", + source: '(3+4)', }); - expect(sol.liftmbM(sol.mulb, "*")(sol.m(3), sol.m(4))).to.deep.equal({ + expect(sol.liftmbM(sol.mulb, '*')(sol.m(3), sol.m(4))).to.deep.equal({ value: 12, - source: "(3*4)", + source: '(3*4)', }); }); }); - describe("liftmb(binary, op)", function () { + describe('liftmb(binary, op)', () => { it(`is a modified function liftmbM that can accept arguments that are either numbers - or m objects`, function () { - expect(sol.liftmb(sol.addb, "+")(3, 4)).to.deep.equal({ + or m objects`, () => { + expect(sol.liftmb(sol.addb, '+')(3, 4)).to.deep.equal({ value: 7, - source: "(3+4)", + source: '(3+4)', }); }); }); - describe("liftm(func, op)", function () { + describe('liftm(func, op)', () => { it(`is a modified function liftmbM that is generalized for any amount of arguments - that can accept arguments that are either numbers or m objects`, function () { - expect(sol.liftm(sol.addb, "+")(sol.m(3), sol.m(4))).to.deep.equal({ + that can accept arguments that are either numbers or m objects`, () => { + expect(sol.liftm(sol.addb, '+')(sol.m(3), sol.m(4))).to.deep.equal({ value: 7, - source: "(3+4)", + source: '(3+4)', }); - expect(sol.liftm(sol.mulb, "*")(sol.m(3), sol.m(4))).to.deep.equal({ + expect(sol.liftm(sol.mulb, '*')(sol.m(3), sol.m(4))).to.deep.equal({ value: 12, - source: "(3*4)", + source: '(3*4)', }); - expect(sol.liftm(sol.mulb, "*")(3, 4)).to.deep.equal({ + expect(sol.liftm(sol.mulb, '*')(3, 4)).to.deep.equal({ value: 12, - source: "(3*4)", + source: '(3*4)', }); }); }); - describe("exp(value)", function () { - it(`evaluates simple array expressions`, function () { + describe('exp(value)', () => { + it('evaluates simple array expressions', () => { assert.equal(sol.exp([sol.mul, 1, 2, 4]), 8); assert.equal(sol.exp(42), 42); }); }); - - describe("expn(value)", function () { - it(`is a modified exp that can evaluate nested array expressions`, function () { + + describe('expn(value)', () => { + it('is a modified exp that can evaluate nested array expressions', () => { assert.equal( sol.expn([Math.sqrt, [sol.add, [sol.square, 3], [sol.square, 4]]]), - 5 + 5, ); // assert.equal(sol.expn(34), 34); }); From 262d625b77c80417744bcd21b6ed0c82530dca71 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Sun, 25 Feb 2024 20:33:41 +0100 Subject: [PATCH 13/15] [ADD] final functions + [FIX] lint for private functions - [ADD] func + test for: addg, - [ADD] func + test for: liftg, - [ADD] func + test for: arrayg, - [ADD] func + test for: continuizeu, - [ADD] func + test for: continuize, - [ADD] func + test for: vector, - [ADD] func + test for: exploitVector, - [ADD] func + test for: vectorSafe, - [ADD] func + test for: pubsub, - [ADD] func + test for: mapRecurse, - [ADD] func + test for: filterRecurse, - [FIX] Lint error on # sign by adding parserOption `ecmaVersion: 13` --- .eslintrc.js | 5 +- Solutions/Bltzz_solution.js | 200 ++++++++++++++++++++++++++++++ test/Bltzz_tests.js | 234 +++++++++++++++++++----------------- 3 files changed, 330 insertions(+), 109 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index f33a9a7..89c281e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -14,5 +14,8 @@ module.exports = { ], "rules": { "no-underscore-dangle": 0, - } + }, + "parserOptions": { + "ecmaVersion": 13, + }, }; \ No newline at end of file diff --git a/Solutions/Bltzz_solution.js b/Solutions/Bltzz_solution.js index 2b43b4f..0eae984 100644 --- a/Solutions/Bltzz_solution.js +++ b/Solutions/Bltzz_solution.js @@ -1112,6 +1112,195 @@ function expn(value) { return undefined; } +/** + * addg(value) ⇒ number | undefined + * + * @param {value} let - an array of any + * @returns the result of the operation + */ +function addg(value) { + let tmpSum = value || 0; + if (value === undefined) return undefined; + + return function adder(arg) { + if (arg === undefined) return tmpSum; + tmpSum += arg; + return adder; + }; +} + +/** + * liftg(binary) ⇒ function + * + * @param {binary} function - a binary function + * @returns a function that takes one addtl value and executes the + * binary on the result and the value + */ +function liftg(binary) { + let tmpRes = 0; + let iterations = 0; + if (binary === undefined) return undefined; + + return function executor(arg) { + if (arg === undefined && iterations === 0) return undefined; + if (arg === undefined) return tmpRes; + tmpRes = (iterations === 0) ? arg : binary(tmpRes, arg); + iterations += 1; + return executor; + }; +} + +/** + * arrayg(value) ⇒ array + * + * @param {value} number - any number + * @returns an array holding all args from earlier invocations + */ +function arrayg(value) { + const tmpRes = value === undefined ? [] : [value]; + if (value === undefined) return tmpRes; + return function executor(arg) { + if (arg === undefined) return tmpRes; + tmpRes.push(arg); + return executor; + }; +} + +/** + * continuizeu(unary) ⇒ function + * + * @param {unary} function - any unary function + * @returns a function that takes a callback and an argument + */ +function continuizeu(unary) { + return function executor(callback, arg) { + callback(unary(arg)); + }; +} + +/** + * continuize(func) ⇒ function + * + * @param {func} function - any unary function + * @returns a function that takes a callback and arguments + */ +function continuize(func) { + return function executor(callback, ...args) { + callback(func(...args)); + }; +} + +/** + * vector() ⇒ function + * + * @returns a vector object + */ +function vector() { + return { + secretArray: [], + append: function append(arg) { + this.secretArray.push(arg); + }, + store: function store(pos, arg) { + this.secretArray[pos] = arg; + }, + get: function get(pos) { + return this.secretArray[pos]; + }, + }; +} + +/** + * exploitVector(v) ⇒ function + * + * @param {v} object - any vector object + * @returns the secret array of the vector object + */ +function exploitVector(v) { + return v.secretArray; +} + +/** + * vectorSafe() ⇒ function + * + * @returns a vector object that denies direct variable access + */ +function vectorSafe() { + class Vector { + #secretArray = []; + + append(arg) { + this.#secretArray.push(arg); + } + + store(pos, arg) { + this.#secretArray[pos] = arg; + } + + get(pos) { + return this.#secretArray[pos]; + } + } + return new Vector(); +} + +/** + * pubsub() + * + * @returns an object with publish/subscribe functionality + */ +function pubsub() { + class PubSub { + #callback; + + subscribe(callback) { + this.#callback = callback; + } + + publish(text) { + this.#callback(text); + } + } + return new PubSub(); +} + +/** + * mapRecurse(array, callback) ⇒ array + * + * @param {array} array - any array of numbers + * @param {callback} function - the callback function + * @returns an a transformation for each element of a given array, recursively + * + * */ +function mapRecurse(array, callback) { + if (array.length === 0) { + return []; + } + // Recursive case: process the first element and concatenate the result with the rest of the array + return [callback(array[0])].concat(mapRecurse(array.slice(1), callback)); +} + +/** + * filterRecurse(array, predicate) ⇒ array + * + * @param {array} array - any array of numbers + * @param {callback} function - the callback function + * @returns an a transformation for each element of a given array, recursively + * + * */ +function filterRecurse(array, predicate) { + if (array.length === 0) { + return []; + } + const firstElement = array[0]; + // Recursive case: process the first element and concatenate the result with the rest of the array + const filteredRest = filterRecurse(array.slice(1), predicate); + // Check if the first element satisfies the predicate + if (predicate(firstElement)) { + return [firstElement].concat(filteredRest); + } + return filteredRest; +} module.exports = { identity, @@ -1182,4 +1371,15 @@ module.exports = { liftm, exp, expn, + addg, + liftg, + arrayg, + continuizeu, + continuize, + vector, + exploitVector, + vectorSafe, + pubsub, + mapRecurse, + filterRecurse, }; diff --git a/test/Bltzz_tests.js b/test/Bltzz_tests.js index 334f71f..3f18a02 100644 --- a/test/Bltzz_tests.js +++ b/test/Bltzz_tests.js @@ -912,111 +912,129 @@ describe('JS_Fun_Practice', () => { sol.expn([Math.sqrt, [sol.add, [sol.square, 3], [sol.square, 4]]]), 5, ); - // assert.equal(sol.expn(34), 34); - }); - }); - // describe("addg(value)", function () { - // it(`adds from many invocations, until it sees an empty invocation`, function () { - // assert.equal(sol.addg(), undefined); - // assert.equal(sol.addg(2)(), 2); - // assert.equal(sol.addg(2)(7)(), 9); - // assert.equal(sol.addg(3)(0)(4)(), 7); - // assert.equal(sol.addg(1)(2)(4)(8)(), 15); - // }); - // }); - // describe("liftg(value)", function () { - // it(`will take a binary function and apply it to many invocations`, function () { - // assert.equal(sol.liftg(sol.mulb)(), undefined); - // assert.equal(sol.liftg(sol.mulb)(3)(), 3); - // assert.equal(sol.liftg(sol.mulb)(3)(0)(4)(), 0); - // assert.equal(sol.liftg(sol.mulb)(1)(2)(4)(8)(), 64); - // }); - // }); - // describe("arrayg(value)", function () { - // it(`will build an array from many invocations`, function () { - // expect(sol.arrayg()).to.deep.equal([]); - // expect(sol.arrayg(3)()).to.deep.equal([3]); - // expect(sol.arrayg(3)(4)(5)()).to.deep.equal([3, 4, 5]); - // }); - // }); - // describe("continuizeu(unary)", function () { - // beforeEach(function () { - // this.sinon.stub(console, "log"); - // }); - - // it(`takes a unary function and returns a function - // that takes a callback and an argument`, function () { - // sol.continuizeu(Math.sqrt)(console.log, 81); - // expect(console.log.calledOnce).to.be.true; - // expect(console.log.calledWith(9)).to.be.true; - // }); - // }); - // describe("continuize(any)", function () { - // beforeEach(function () { - // this.sinon.stub(console, "log"); - // }); - - // it(`takes a function and returns a function - // that takes a callback and an argument`, function () { - // sol.continuize(sol.mul)(console.log, 81, 4, 2); - // expect(console.log.calledOnce).to.be.true; - // expect(console.log.calledWith(648)).to.be.true; - // }); - // }); - // describe("vector()", function () { - // it(`is an array wrapper object with methods get, store, and append, such that an - // attacker cannot get access to the private array`, function () { - // let v = sol.vector(); - // v.append(7); - // v.store(1, 8); - // assert.equal(v.get(0), 7); - // assert.equal(v.get(1), 8); - // }); - // }); - // describe("exploitVector()", function () { - // it(`accesses array outside of vector`, function () { - // let v = sol.vector(); - // v.append(1); - // v.append(2); - // expect(sol.exploitVector(v)).to.deep.equal([1, 2]); - // }); - // }); - // describe("vectorSafe()", function () { - // it(`can't access array outside of vector`, function () { - // let v = sol.vectorSafe(); - // v.append(1); - // v.append(2); - // expect(sol.exploitVector(v)).to.deep.equal(undefined); - // }); - // }); - // describe("pubsub()", function () { - // beforeEach(function () { - // this.sinon.stub(console, "log"); - // }); - - // it(`makes a publish/subscribe object. It will reliably deliver all publications - // to all subscribers in the right order`, function () { - // let ps = sol.pubsub(); - // ps.subscribe(console.log); - // ps.publish("It works!"); - - // expect(console.log.calledOnce).to.be.true; - // expect(console.log.calledWith("It works!")).to.be.true; - // }); - // }); - // describe("mapRecurse(array, predicate)", function () { - // it(`performs a transformation for each element of a given array, recursively`, function () { - // expect(sol.mapRecurse([1, 2, 3, 4], (x) => x * 2)).to.deep.equal([ - // 2, 4, 6, 8, - // ]); - // }); - // }); - // describe("filterRecurse(array, predicate)", function () { - // it(`takes in an array and a predicate function and returns a new array by - // filtering out all items using the predicate, recursively.`, function () { - // expect(sol.filterRecurse([1, 2, 3, 4], (x) => x % 2 === 0)).to.deep.equal( - // [2, 4] - // ); - // }); - // }); -}); + assert.equal(sol.expn(34), 34); + }); + }); + + describe('addg(value)', () => { + it('adds from many invocations, until it sees an empty invocation', () => { + assert.equal(sol.addg(), undefined); + assert.equal(sol.addg(2)(), 2); + assert.equal(sol.addg(2)(7)(), 9); + assert.equal(sol.addg(3)(0)(4)(), 7); + assert.equal(sol.addg(1)(2)(4)(8)(), 15); + }); + }); + + describe('liftg(value)', () => { + it('will take a binary function and apply it to many invocations', () => { + assert.equal(sol.liftg(sol.mulb)(), undefined); + assert.equal(sol.liftg(sol.mulb)(3)(), 3); + assert.equal(sol.liftg(sol.mulb)(3)(0)(4)(), 0); + assert.equal(sol.liftg(sol.mulb)(1)(2)(4)(8)(), 64); + }); + }); + + describe('arrayg(value)', () => { + it('will build an array from many invocations', () => { + expect(sol.arrayg()).to.deep.equal([]); + expect(sol.arrayg(3)()).to.deep.equal([3]); + expect(sol.arrayg(3)(4)(5)()).to.deep.equal([3, 4, 5]); + }); + }); + + describe('continuizeu(unary)', () => { + beforeEach(function invocation() { + this.sinon.stub(console, 'log'); + }); + it(`takes a unary function and returns a function + that takes a callback and an argument`, () => { + sol.continuizeu(Math.sqrt)(console.log, 81); // eslint-disable-line no-console + expect(console.log.calledOnce).to.be.true; // eslint-disable-line + // no-console, no-unused-expressions + expect(console.log.calledWith(9)).to.be.true; // eslint-disable-line + // no-console, no-unused-expressions + }); + }); + + describe('continuize(any)', () => { + beforeEach(function invocation() { + this.sinon.stub(console, 'log'); + }); + + it(`takes a function and returns a function + that takes a callback and an argument`, () => { + sol.continuize(sol.mul)(console.log, 81, 4, 2);// eslint-disable-line + // no-console, no-unused-expressions + expect(console.log.calledOnce).to.be.true;// eslint-disable-line + // no-console, no-unused-expressions + expect(console.log.calledWith(648)).to.be.true;// eslint-disable-line + // no-console, no-unused-expressions + }); + }); + + describe('vector()', () => { + it(`is an array wrapper object with methods get, store, and append, such that an + attacker cannot get access to the private array`, () => { + const v = sol.vector(); + v.append(7); + v.store(1, 8); + assert.equal(v.get(0), 7); + assert.equal(v.get(1), 8); + }); + }); + + describe('exploitVector()', () => { + it('accesses array outside of vector', () => { + const v = sol.vector(); + v.append(1); + v.append(2); + expect(sol.exploitVector(v)).to.deep.equal([1, 2]); + }); + }); + + describe('vectorSafe()', () => { + it('can\'t access array outside of vector', () => { + const v = sol.vectorSafe(); + v.append(1); + v.append(2); + expect(sol.exploitVector(v)).to.deep.equal(undefined); + }); + }); + + describe('pubsub()', () => { + beforeEach(function invocation() { + this.sinon.stub(console, 'log'); + }); + + it(`makes a publish/subscribe object. It will reliably deliver all publications + to all subscribers in the right order`, () => { + const ps = sol.pubsub(); + ps.subscribe(console.log);// eslint-disable-line + // no-console, no-unused-expressions + ps.publish('It works!'); + + expect(console.log.calledOnce).to.be.true;// eslint-disable-line + // no-console, no-unused-expressions + expect(console.log.calledWith("It works!")).to.be.true;// eslint-disable-line + // no-console, no-unused-expressions + }); + }); + + describe('mapRecurse(array, predicate)', () => { + it('performs a transformation for each element of a given array, recursively', () => { + expect(sol.mapRecurse([1, 2, 3, 4], x => x * 2)).to.deep.equal([ + 2, 4, 6, 8, + ]); + }); + }); + + describe('filterRecurse(array, predicate)', () => { + it(`takes in an array and a predicate function and returns a new array by + filtering out all items using the predicate, recursively.`, () => { + expect(sol.filterRecurse([1, 2, 3, 4], x => x % 2 === 0)).to.deep.equal( + [2, 4], + ); + }); + }); +}); \ No newline at end of file From b799b71e094a8303d51bfb13eeef03244faabec9 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Sun, 25 Feb 2024 20:39:31 +0100 Subject: [PATCH 14/15] [RM] lint from package.json to prevent other implementations from failing. --- package.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/package.json b/package.json index 3c7e876..7c6a34d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,6 @@ "description": "A testing utility for the fun javascript functions", "main": "index.js", "scripts": { - "lint": "eslint **/Bltzz_*.js", "test": "mocha || true" }, "repository": { @@ -19,9 +18,6 @@ "homepage": "https://github.com/hazamaswag/JS_Fun_Practice#readme", "devDependencies": { "chai": "^4.2.0", - "eslint": "^8.56.0", - "eslint-config-airbnb-base": "^11.2.0", - "eslint-plugin-import": "^2.8.0", "mocha": "^10.2.0", "mocha-sinon": "^2.1.2", "sinon": "^9.0.2" From 813905e3053d38e82925f89a750d33d82951b618 Mon Sep 17 00:00:00 2001 From: Bltzz Date: Sun, 25 Feb 2024 20:40:51 +0100 Subject: [PATCH 15/15] [RM] sinon-chai dependency --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index 7c6a34d..213c679 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,5 @@ "mocha": "^10.2.0", "mocha-sinon": "^2.1.2", "sinon": "^9.0.2" - }, - "dependencies": { - "sinon-chai": "^3.7.0" } }