Passing additional vs one argument into the function - javascript

I am implementing compose function using reduceRight menthod as follows
const compose = fns => (...args) =>
fns.reduceRight((acc, fn) => fn(acc, ...[args.slice(1)]), args[0]);
const func3 = (x, y) => (y > 0 ? x + 3 : x - 3);
const func2 = x => x ** 2;
const func1 = x => x - 8;
const fnOne = compose([func1, func2, func3])('3', 1);
console.log(fnOne); // should be 1081
const fnTwo = compose([func1, func2, func3])('3', -1);
console.log(fnTwo); //should be -8
the first function is supposed to receive two arguments and return the result to the next function as the only one argument. The problem is that the first function is passing two arguments to the next function instead of one. Let me know if you have any ideas how to fix it. Any help is very much appreciated.

The problem here is that you're not modifying the args variable.
Let's look at what happens in detail:
At the first call of your reductor, acc becomes func3(args[0], ...[args.shift(1)]) === func3(args[0], args[1], args[2], ...).
At the second call, acc becomes func2(acc, [args.shift(1)]), which is func2(func3(args[0], args[1], args[2], ...), args[1], args[2], ...).
You can already see where the problem lies: args1 is never dropped from the array, because Array.slice() creates a copy and does not modify the actual array.
To solve your problem you should instead use:
const compose = fns => (...args) =>
fns.reduceRight((acc, fn) => fn(acc, ...args.splice(0, fn.length - 1)), args[0]);

You need to call the first function outside the reduceRight() loop, since it's not being called the same way as all the other functions. It gets its arguments from ...args and its value should be used as the initial accumulator argument to reduce.
const compose = fns => (...args) => {
let last = fns.pop();
return fns.reduceRight((acc, fn) => fn(acc), last(...args))
};
const func3 = function(x, y) {
console.log(`func3 got ${arguments.length} arguments`);
return (y > 0 ? x + 3 : x - 3);
};
const func2 = function(x) {
console.log(`func2 got ${arguments.length} arguments`);
return x ** 2;
};
const func1 = function(x) {
console.log(`func2 got ${arguments.length} arguments`);
return x - 8;
};
const fnOne = compose([func1, func2, func3])('3', 1);
console.log(fnOne); // should be 1081
const fnTwo = compose([func1, func2, func3])('3', -1);
console.log(fnTwo); //should be -8

Related

How would i make a function for it as well which will keep track of the empty values of second argument

multiply(10)()()()(12)
This a function call and we have to make a function to multiply 2 numbers
You can curry the function and leverage rest parameters and spreading. If there are no arguments supplied then (...args) will be an empty array and .bind(this, ...args) will create a function without partially applying any values to it.
Once all arguments are satisfied, the function is immediately called.
const curry = f => function(...args) {
if (f.length - args.length <= 0)
return f.apply(this, args);
return curry(f.bind(this, ...args));
}
const mul = (a, b) => a * b;
const multiply = curry(mul);
console.log(multiply(10, 12)); // = 120
console.log(multiply(10)(12)); // = 120
console.log(multiply(10)()(12)); // = 120
console.log(multiply(10)()()()(12)); // = 120
const obj = {
x: 2,
multiply: curry(function(a, b) {
return a * b * this.x;
})
}
console.log(obj.multiply(10)()()()(12)); // = 240

Function composition with multiple arguments using reduceRight() - JavaScript

I am trying to implement function composition with multiple arguments using reduceRight. This question has been answered at this link but slightly with a different input data. For some reason I am getting the following error : Uncaught TypeError: fn is not a function. Avoid using func3 = ([x, y]) => (y > 0 ? x + 3 : x - 3); in this format. Let me know if there is a way to fix it.
const compose = (...fns) => (...args) =>
fns.reduceRight((acc, fn) => fn(acc), args);
const func3 = (x, y) => (y > 0 ? x + 3 : x - 3);
//const func3 = ([x, y]) => (y > 0 ? x + 3 : x - 3); - avoid using this version
const func2 = x => x ** 2;
const func1 = x => x - 8;
const fnOne = compose([func1, func2, func3])('3', 1);
console.log(fnOne); // should be 1081
const fnTwo = compose([func1, func2, func3])('3', -1);
console.log(fnTwo); //should be -8
[1]: https://stackoverflow.com/questions/55842990/multiple-arguments-composible-function-implementation-using-reduceright
SOLUTION
Array#reduceRight only has support for one currentValue so we can't have a function accept multiple arguments. The only way would be to avoid using reduceRight for the first call by popping it from the array and calling it manual instead of letting reduceRight do it:
First approach
const compose = fns => (...args) =>
fns.reduceRight((acc, fn) => fn(acc), fns.pop()(...args));
Second approach
const compose = fns => (...args) =>
fns.reduceRight((acc, fn) => fn(...[acc, ...args.slice(1)]), args[0]);
You're passing an array into compose instead of a function (or multiple functions), so fn is an array and you get that error when you try to use it as a function.
Either replace ...fns with fns (so that compose expects an array) or remove the square brackets in your calls to compose. Also use the version of func3 that you have commented out:
const compose = (...fns) => (...args) =>
fns.reduceRight((acc, fn) => fn(acc), args);
const func3 = ([x, y]) => (y > 0 ? x + 3 : x - 3);
const func2 = x => x ** 2;
const func1 = x => x - 8;
const fnOne = compose(func1, func2, func3)('3', 1);
console.log(fnOne); // should be 1081
const fnTwo = compose(func1, func2, func3)('3', -1);
console.log(fnTwo); //should be -8
If you don't can or want to convert func3 to a single argument function. You could spread the args when calling fn. Then capture the result in an array so it can be spread over the next fn call. When returning the final result simply take the first element of the resulting array.
const compose = (...fns) => (...args) =>
fns.reduceRight((args, fn) => [fn(...args)], args)[0];
const func3 = (x, y) => (y > 0 ? x + 3 : x - 3);
// const func3 = ([x, y]) => (y > 0 ? x + 3 : x - 3); - avoid using this version
const func2 = x => x ** 2;
const func1 = x => x - 8;
const fnOne = compose(func1, func2, func3)('3', 1);
console.log(fnOne); // should be 1081
const fnTwo = compose(func1, func2, func3)('3', -1);
console.log(fnTwo); //should be -8
You need provide to compose function three parameters/function instead a array.
func3 will be the first function performed, so it will receive the array args. This array need be destructure in x and y variable. This reduce can be make in the parameters definition, like this: ([x, y]).
const compose = (...fns) => (...args) =>
fns.reduceRight((acc, fn) => fn(acc), args);
const func3 = ([x, y]) => y > 0 ? x + 3 : x - 3;
const func2 = x => x ** 2;
const func1 = x => x - 8;
const fnOne = compose(func1, func2, func3)('3', 1);
console.log(fnOne); // should be 1081
const fnTwo = compose(func1, func2, func3)('3', -1);
console.log(fnTwo); //should be -8

Recursively calling a curried function in Javascript

As a toy example lets say we have this function and its usage:
const map = (f = n => n + 1) => (lst = [1,2,3]) => {
if(lst.length === 0)
return [];
else
return [f(...lst.splice(0,1)), ...map(f)(lst)];
}
const inc = n => n + 1;
const map_inc = map(inc);
map_inc([1,2,3]) // => (produces) [2,3,4]
Inside of the curried function map I am using "recursion" by calling map(f)(lst).
The example above rebuilds the function before it can be called.
Is it possible to do this recursion without rebuilding the function?
I know of this way:
y = (f = (f, ...args) => [...args],
...args) => f(f, ...args);
const map = (mapper = n => n + 1) => (self = mapper, lst = [1,2,3]) => {
if(lst.length === 0)
return [];
else
return [mapper(...lst.splice(0,1)), ...self(self, lst)];
}
const inc = n => n + 1;
const map_inc = (...args) => y(map(inc), ...args);
map_inc([1,2,3]) // => (produces) [2,3,4]
I do not really like how this requires the passing of the function to itself.
Can this be done without the y function and without passing the function to itself? Can this be done in a more point-free style?
If I'm understanding your question correctly, you can't return named arrow functions, but you can return a named regular function and call it recursively like this:
const reducer = k => function recurse(a, item) {
//...
const s_res = _.split(item, k, 1);
return recurse(a.withMutations(a => {
a.push(s_res[0]);
let a_element = document.createElement('a');
a_element.setAttribute('href', '#');
a_element.addEventListener('click', () => display_gen_element(k, obj));
a.push(a_element);
}), s_res[1]);
};
P.S. For the sake of readability please don't use one-letter variable names unless it's blindingly obvious what they're for, e.g. a counter in a for loop, etc.
If your purpose was remove the need to pass self to itself
...self(self, lst)
You can do this by adding 1 more function named recursor
const map = (mapper = n => n + 1) => (lst = [1, 2, 3]) => {
const recursor = lst => {
if (lst.length === 0) return [];
else return [mapper(...lst.splice(0, 1)), ...recursor(lst)];
};
return recursor(lst);
};
const inc = n => n + 1;
const map_inc = map(inc);
console.log(map_inc([1, 2, 3])); // => (produces) [2,3,4]
You didn't need the y combinator-like function called y at all.
recursor has mapper in its closure

How to correctly serialize Javascript curried arrow functions?

const makeIncrementer = s=>a=>a+s
makeIncrementer(10).toString() // Prints 'a=>a+s'
which would make it impossible to de-serialize correctly (I would expect something like a=>a+10 instead.
Is there a way to do it right?
This is a great question. While I don't have a perfect answer, one way you could get details about the argument/s is to create a builder function that stores the necessary details for you. Unfortunately I can't figure out a way to know which internal variables relate to which values. If I figure out anything else i'll update:
const makeIncrementer = s => a => a + s
const builder = (fn, ...args) => {
return {
args,
curry: fn(...args)
}
}
var inc = builder(makeIncrementer, 10)
console.log(inc) // logs args and function details
console.log(inc.curry(5)) // 15
UPDATE: It will be a mammoth task, but I realised, that if you expand on the builder idea above, you could write/use a function string parser, that could take the given args, and the outer function, and rewrite the log to a serialised version. I have a demo below, but it will not work in real use cases!. I have done a simple string find/replace, while you will need to use an actual function parser to replace correctly. This is just an example of how you could do it. Note that I also used two incrementer variables just to show how to do multiples.
function replaceAll(str, find, replace) {
return str.replace(new RegExp(find, 'g'), replace)
}
const makeIncrementer = (a, b) => c => c + a + b
const builder = (fn, ...args) => {
// get the outer function argument list
var outers = fn.toString().split('=>')[0]
// remove potential brackets and spaces
outers = outers.replace(/\(|\)/g,'').split(',').map(i => i.trim())
// relate the args to the values
var relations = outers.map((name, i) => ({ name, value: args[i] }))
// create the curry
var curry = fn(...args)
// attempt to replace the string rep variables with their true values
// NOTE: **this is a simplistic example and will break easily**
var serialised = curry.toString()
relations.forEach(r => serialised = replaceAll(serialised, r.name, r.value))
return {
relations,
serialised,
curry: fn(...args)
}
}
var inc = builder(makeIncrementer, 10, 5)
console.log(inc) // shows args, serialised function, and curry
console.log(inc.curry(4)) // 19
You shouldn't serialize/parse function bodies since this quickly leads to security vulnerabilities. Serializing a closure means to serialize its local state, that is you have to make the closure's free variables visible for the surrounding scope:
const RetrieveArgs = Symbol();
const metaApply = f => x => {
const r = f(x);
if (typeof r === "function") {
if (f[RetrieveArgs])
r[RetrieveArgs] = Object.assign({}, f[RetrieveArgs], {x});
else r[RetrieveArgs] = {x};
}
return r;
}
const add = m => n => m + n,
f = metaApply(add) (10);
console.log(
JSON.stringify(f[RetrieveArgs]) // {"x":10}
);
const map = f => xs => xs.map(f)
g = metaApply(map) (n => n + 1);
console.log(
JSON.stringify(g[RetrieveArgs]) // doesn't work with higher order functions
);
I use a Symbol in order that the new property doesn't interfere with other parts of your program.
As mentioned in the code you still cannot serialize higher order functions.
Combining ideas from the two answers so far, I managed to produce something that works (though I haven't tested it thoroughly):
const removeParentheses = s => {
let match = /^\((.*)\)$/.exec(s.trim());
return match ? match[1] : s;
}
function serializable(fn, boundArgs = {}) {
if (typeof fn !== 'function') return fn;
if (fn.toJSON !== undefined) return fn;
const definition = fn.toString();
const argNames = removeParentheses(definition.split('=>', 1)[0]).split(',').map(s => s.trim());
let wrapper = (...args) => {
const r = fn(...args);
if (typeof r === "function") {
let boundArgsFor_r = Object.assign({}, boundArgs);
argNames.forEach((name, i) => {
boundArgsFor_r[name] = serializable(args[i]);
});
return serializable(r, boundArgsFor_r);
}
return r;
}
wrapper.toJSON = function () {
return { function: { body: definition, bound: boundArgs } };
}
return wrapper;
}
const add = m => m1 => n => m + n * m1,
fn = serializable(add)(10)(20);
let ser1, ser2;
console.log(
ser1 = JSON.stringify(fn) // {"function":{"body":"n => m + n * m1","bound":{"m":10,"m1":20}}}
);
const map = fn => xs => xs.map(fn),
g = serializable(map)(n => n + 1);
console.log(
ser2 = JSON.stringify(g) // {"function":{"body":"xs => xs.map(fn)","bound":{"fn":{"function":{"body":"n => n + 1","bound":{}}}}}}
);
const reviver = (key, value) => {
if (typeof value === 'object' && 'function' in value) {
const f = value.function;
return eval(`({${Object.keys(f.bound).join(',')}}) => (${f.body})`)(f.bound);
}
return value;
}
const rev1 = JSON.parse(ser1, reviver);
console.log(rev1(5)); // 110
const rev2 = JSON.parse(ser2, reviver);
console.log(rev2([1, 2, 3])); // [2, 3, 4]
This works for arrow functions, that do not have default initializers for the arguments. It supports higher order functions as well.
One still has to be able to wrap the original function into serializable before applying it to any arguments though.
Thank you #MattWay and #ftor for valuable input !

Currying with functions that take unlimited arguments

Lets say i have the following add function that takes an unlimited number of arguments.
function add () {
var total = 0;
var args = Array.prototype.slice.call(arguments, 0);
for (var i=0; i<args.length; i++) {
total += arguments[i];
}
return total;
}
and the following curry function.
function curryFunction(orig_func) {
var ap = Array.prototype;
var args = arguments;
function fn() {
if (arguments.length != 0) {
ap.push.apply(fn.args, arguments);
return fn;
} else {
return orig_func.apply(this, fn.args);
}
};
return function() {
fn.args = ap.slice.call( args, 1 );
return fn.apply( this, arguments );
};
}
I then want to do something like:
var f = curryFunction(add);
var a = f(3)(4)(3);
var b = f(10)(3);
var result1 = a(); // returns 10
var result2 = b(); // returns 13
However i always get 13 for both a() and b() i assume is because in line
fn.args = ap.slice.call(args, 1);
the existing array [3,4,3] is overwriting with []. Can someone please provide me with a hint on how to make this work? Thanks
The problem is that fn is scoped to curryFunction and so is shared between a and b.
All you have to do is move the definition of fn into the anonymous return function. It's then created when you call f, and the problematic fn.args = line is only called once.
Proof: jsFiddle.
Currying a function which takes indefinitely many arguments can be implemented as follows;
Lets say we have a function called addAll() which returns the sum of all provided arguments.
var addall = (...a) => a.reduce((p,c) => p + c);
And we have a curry function which takes a function and returns curried version ad infinitum up until the returned function is called with no arguments, only when the result of all previously provided arguments will be returned. OK here is the curry function.
var curry = f => (...a) => a.length ? curry(f.bind(f,...a))
: f();
Lets see it in action;
var addAll = (...a) => a.reduce((p,c) => p + c),
curry = f => (...a) => a.length ? curry(f.bind(f,...a)) : f(),
curried = curry(addAll),
result = curried(10,11)(10)(37)(10,17,42)();
console.log(result);
result = curried("a","b")("c")("d")("e","f","g")();
console.log(result);

Categories

Resources