Is there some way to "wrap" a recursive function via a higher-order function, so that the recursive call is also wrapped? (e.g. to log the arguments to the function on each call.)
For example, suppose we have a function, sum(), that returns the sum of a list of numbers by adding the head to the sum of the tail:
function sum(a) {
if (a.length === 0) {
return 0;
} else {
return a[0] + sum(a.slice(1));
}
}
Is there some way to write a higher-order function, logging(), that takes the sum() function as input, and returns a function that outputs the arguments to sum() on each recursive call?
The following does not work:
function logging(fn) {
return function(a) {
console.log(a);
return fn(a);
}
}
sum2 = logging(sum);
sum2([1, 2, 3]);
Actual output:
[1, 2, 3]
-> 6
Expected output:
[1, 2, 3]
[2, 3]
[3]
[]
-> 6
Is this even possible if sum() is rewritten so that it can be used with Y Combinator-style "recursion"?
function sum_core(g) {
return function (a) {
if (a.length === 0) {
return 0;
} else {
return a[0] + g(a.slice(1));
}
};
}
sum = Y(sum_core);
sum([1, 2, 3]);
// -> 6
I know this is kind of a non-answer but what you want is much easier to do if you use objects and dynamically dispatched methods. Essentially, we store "rec" in a mutable cell instead of having to pass it around.
It would be kind of similar to sum = logging(sum) (instead of sum2 =) but is more idiomatic and doesn't hardcode the name for the mutable reference we dispatch on.
var obj = {
sum : function(a){
if (a.length === 0) {
return 0;
} else {
return a[0] + this.sum(a.slice(1)); // <-- dispatch on `this`
}
}
}
function add_logging(obj, funcname){
var oldf = obj[funcname];
obj[funcname] = function(/**/){
console.log(arguments);
return oldf.apply(this, arguments);
}
}
add_logging(obj, 'sum');
Let's start backwards. Say you want a function traceSum:
> traceSum([1, 2, 3]);
[1, 2, 3]
[2, 3]
[3]
[]
6
You could implement traceSum as follows:
function traceSum(a) {
console.log(a);
if (a.length === 0) return 0;
else return a[0] + traceSum(a.slice(1));
}
From this implementation we can create a generalized trace function:
function trace(f) {
return function (a) {
console.log(a);
return f(trace(f), a);
};
}
This is similar to the way the Y combinator is implemented in JavaScript:
function y(f) {
return function (a) {
return f(y(f), a);
};
}
Your traceSum function can now be implemented as:
var traceSum = trace(function (traceSum, a) {
if (a.length === 0) return 0;
else return a[0] + traceSum(a.slice(1));
});
Unfortunately if you can't modify the sum function then you can't trace it since you need some way to specify which function to callback dynamically. Consider:
function sum(a) {
if (a.length === 0) return 0;
else return a[0] + sum(a.slice(1));
}
You cannot trace the above function because inside the function sum will always refer to the function itself. There's no way to specify the value of sum dynamically.
function sum(a) {
if (a.length === 0) {
return 0;
} else {
return a[0] + sum(a.slice(1));
}
}
var dummySum = sum, sum = function(args) {
console.log(args);
return dummySum(args);
};
console.log(sum([1, 2, 3]));
Output
[ 1, 2, 3 ]
[ 2, 3 ]
[ 3 ]
[]
6
If you insist on using regular functions without using "this", the only way I can think of is applying the logging combinator before you tie the knot with the recursion (Y) combinator.
Basically, we need to use dynamic dispatching in the logger just like we used dynamic dispatching in the sum function itself.
// f: function with a recursion parameter
// rec: function without the recursion parameter
var sum = function(rec, a){
if (a.length === 0) {
return 0;
} else {
return a[0] + rec(a.slice(1));
}
};
var logging = function(msg, f){
return function(rec, x){
console.log(msg, x);
return f(rec, x);
}
}
var Y = function(f){
var rec = function(x){
return f(rec, x);
}
return rec;
}
//I can add a bunch of wrappers and only tie the knot with "Y" in the end:
console.log( Y(logging("a", logging("b", sum)))([1,2,3]) );
Output
a [1, 2, 3]
b [1, 2, 3]
a [2, 3]
b [2, 3]
a [3]
b [3]
a []
b []
6
We could also extend Y and logging to be variadic but it would make the code a bit more complicated.
If you cannot change the sum function
function sum(a) {
if (a.length === 0) {
return 0;
} else {
return a[0] + sum(a.slice(1));
}
}
then it's impossible. Sorry
edit
Ugly but works. Don't do that ^^
function rest(a) {
console.log(a);
sum(a, rest);
}
function sum(a, rest) {
if (a.length === 0) {
return 0;
} else {
return a[0] + rest(a.slice(1));
}
}
But looks at http://www.nczonline.net/blog/2009/01/20/speed-up-your-javascript-part-2/
Search for memoizer, I'll read it too.
It is not possible in JavaScript without modifying the function. If you don't want the manual work, your best bet is something like that:
function logged(func){
return eval("("+func.toString().replace(/function(.*){(.*)/g,"function$1{console.log(arguments);$2")+")");
};
Then you can use it like:
function sum(a) {
if (a.length === 0) {
return 0;
} else {
return a[0] + sum(a.slice(1));
}
}
console.log(logged(sum)([1,2,3,4]));
Which outputs:
{ '0': [ 1, 2, 3, 4 ] }
{ '0': [ 2, 3, 4 ] }
{ '0': [ 3, 4 ] }
{ '0': [ 4 ] }
{ '0': [] }
10
logged itself is very slow because it recompiles the function (with eval), but the resulting function is as fast as if you defined it manually. So, use logged only once per function and you are fine.
Scope issue. Try to do the following:
function logging(fn) {
var _fn = fn; // local cached copy
return function(a) {
console.log(a);
return _fn(a);
}
}
Related
how to define a function called countdown with one parameter (n). The function should use recursion to return an array containing the integers n through 1 based on the n parameter. If the function is called with a number less than 1, the function should return an empty array. For example, calling this function with n = 5 should return the array [5, 4, 3, 2, 1]. Your function must use recursion by calling itself and must not use loops of any kind.
function countdown(n) {
return [n].concat(n > 1 ? countdown(n - 1) : []);
}
Here's one way to do it:
function countdown(n) {
if (n < 1) {
return [];
}
if (n === 1) {
return [1];
}
return [n].concat(countdown(n-1));
}
console.log(countdown(0));
console.log(countdown(1));
console.log(countdown(10));
// CountUp instead of countdown:
// Example output : [1, 2, 3, 4, 5]
function countup(n) {
if (n < 1) {
return [];
} else {
const countArray = countup(n - 1);
countArray.push(n);
return countArray;
}
}
console.log(countup(5)) // output : [1, 2, 3, 4, 5]
I've been searching for a (better) way to do the following. Take two arrays and return only the unique values. E.g. ['value1', 'value2'] and ['value2', 'value3'] should return 'value1' and 'value3'. The following seems to work but I was wondering if there was an easier/more concise way to achieve the same result.
function diffArray(arr1, arr2) {
var newArr = arr1.filter(function(a) {
var count = 0;
arr2.forEach(function (x) {
if (x === a) {count++;}
});
if (count !== 0) {return false;}
return true;
}).concat(arr2.filter(function(b) {
var count = 0;
arr1.forEach(function(y) {
if (y === b) {count++;}
});
if (count !== 0) {return false;}
return true;
}));
return newArr;
}
console.log(diffArray([1, 2, 3, 5], [1, 2, 3, 4, 5]));
//Should return only 4.
A fixed version of Th0rndike's answer:
var a1=[1,2];
var a2=[2,3];
function diffArray(a1,a2){
return a2.filter(function(item){
return a1.indexOf(item) < 0;
}).concat(a1.filter(function(item){
return a2.indexOf(item) < 0;
}));
}
console.log(diffArray(a1,a2));
If you can use a library i suggest to use lodash.
The have a generic N array operation for that.
_.xor([2, 1], [2, 3]);
https://lodash.com/docs/4.17.2#xor
I tried to remove some words according to the passing variable.
However, I wrote two versions of code which have minor different!
And they resulted in different kinds of output which I don't understand why!
So I need u guys help, and big big thanks for u guys!
This function will be accepting the different numbers of variables,
which might be ( [arr],1,2 ) or ( [arr],1,2,8,9...) etc,
and remove the the variable in the first array according to the passing numbers.
For example: destroyer ( [1, 2, 3, 4], 2, 3 ) --> output should be [1,4]
And here is my code. ( Notice the minor difference with bold fonts! )
function destroyer(arr) {
for ( var i = 1; i < arguments.length; i++ ){
arr = arguments[0].filter(function(value){
if( value == arguments[i]){
return false;
}else{
return true;
}
});
}
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
The output will be [1,2,3,1,2,3], which means value == arguments[i] doesn't work. However,
function destroyer(arr) {
for ( var i = 1; i < arguments.length; i++ ){
filter = arguments[i];
arr = arguments[0].filter(function(value){
if( value == filter){
return false;
}else{
return true;
}
});
}
return arr;
}
destroyer([1, 2, 3, 1, 2, 3], 2, 3);
This version works perfectly showing me [1,1].
So what's going wrong with the first version??? Thank you!!
The problem with the first one is that arguments applies to the .filter() callback function (the closest function scope, not the parent function scope) so arguments[i] is not what you want it to be.
You could copy those arguments into an actual array which you could then refer to from the .filter() callback.
function destroyer(arr) {
var args = [].slice.call(arguments, 1);
for ( var i = 0; i < args.length; i++ ){
arr = arr.filter(function(value){
if( value == args[i]){
return false;
}else{
return true;
}
});
}
return arr;
}
var a = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
// show output
document.write(JSON.stringify(a));
Personally, I'd suggest a bit simpler version:
function destroyer(arr) {
var args = [].slice.call(arguments, 1);
return arr.filter(function(value) {
return args.indexOf(value) === -1;
});
}
var a = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
// show output
document.write(JSON.stringify(a));
Which, can be written simpler in ES6 using the spread operator:
function destroyer(arr, ...args) {
return arr.filter(function(value) {
return args.indexOf(value) === -1;
});
}
var a = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
// show output
document.write(JSON.stringify(a));
Or, if you prefer even shorter ES6 notation and want to use the new Array.prototype.includes():
function destroyer(arr, ...args) {
return arr.filter(value => !args.includes(value));
}
var a = destroyer([1, 2, 3, 1, 2, 3], 2, 3);
document.write(JSON.stringify(a));
I would write it like that in an es6 version:
function destroyer( arr, ...elem ) {
return arr.reduce( ( prev, next, index ) => elem.includes( arr[index] ) ? prev : prev.concat(next) , [])
}
Is there a JavaScript equivalent to Clojure's "reductions" function or Python's itertools.accumulate? In other words, given an array [x_0, x_1, x_2 ... x_n-1] and a function f(prev, next), it would return an array of length n with values:
[x_0, f(x_0, x_1), f(f(x_0, x_1), x_2)... f(f(f(...)), x_n)]
I'm simulating the desired behavior below:
function accumsum(prev, next) {
last = prev[prev.length - 1] || 0;
prev.push(last + next);
return prev;
}
var x = [1, 1, 1, 1];
var y = x.reduce(accumsum, []);
var z = y.reduce(accumsum, []);
console.log(x);
console.log(y);
console.log(z);
which displays:
[ 1, 1, 1, 1 ]
[ 1, 2, 3, 4 ]
[ 1, 3, 6, 10 ]
But I'm wondering if there is a way to write something simpler like
[1, 1, 1, 1].reductions(function(prev, next) {return prev + next;});
If not, is there a more idiomatic way to do this in JavaScript than what I wrote?
var a = [1, 1, 1, 1];
var c = 0;
a.map(function(x) { return c += x; })
// => [1, 2, 3, 4]
a.reduce(function(c, a) {
c.push(c[c.length - 1] + a);
return c;
}, [0]).slice(1);
// => [1, 2, 3, 4]
I'd use the first one, personally.
EDIT:
Is there a way of doing your first suggestion that doesn't require me to have a random global variable (c in this case) floating around? If I forgot to re-initialize c back to 0, the second time I wrote a.map(...) it would give the wrong answer.
Sure - you can encapsulate it.
function cumulativeReduce(fn, start, array) {
var c = start;
return array.map(function(x) {
return (c = fn(c, x));
});
}
cumulativeReduce(function(c, a) { return c + a; }, 0, [1, 1, 1, 1]);
// => [1, 2, 3, 4]
c
// => ReferenceError - no dangling global variables
I wrote a stateless version
function reductions(coll, reducer, init) {
if (!coll.length) {
return [init]
}
if (init === undefined) {
return reductions(_.drop(coll, 1), reducer, _.first(coll))
}
return [init].concat(reductions(_.drop(coll, 1), reducer, reducer(init, _.first(coll))))
}
For posterity, if you're in a situation where you're using an older version of JavaScript, or don't have access to Underscore.
It's not difficult to implement from scratch and has some educational value.
Here's one way to do it:
function reduce(a, fn, memo) {
var i;
for (i = 0; i < a.length; ++i) {
if ( typeof memo === 'undefined' && i === 0 ) memo = a[i];
else memo = fn(memo, a[i]);
}
return memo;
}
Also, other higher order functions can be written in terms of reduce, e.g. "map", shown here:
function map(a, fn) {
return reduce(a, function(memo, x) {
return memo.concat(fn(a));
}, []);
}
for reference the equivalent imperative (and faster) version of map would be:
function map2(a, fn) {
var newA = [], i;
for (i = 0; i < a.length; ++i) {
newA.push(fn(a[i]));
}
return newA;
}
I need a function that would return a value from an array that may contain any number of arrays. It should be called like getValueFromArray(array, [2, 4]) - this example should return 4th element of the 2d array of the passed array.
Here is my code:
function getValueFromArray(arr, indexes){
var val,
currentIndex = indexes[0];
if(!arr[currentIndex] || arr[currentIndex] === '') return value = '';
indexes.splice(0, 1);
if(arr[currentIndex].length)
getValueFromArray(arr[currentIndex], indexes);
else {
val = arr[currentIndex];
return val;
}
}
var y = getValueFromArray([[1,2,3,4], [1,2,3,4]], [0, 2]); // should return 3
var x = getValueFromArray([[1,2,3,4], [1,2,3,4], [5,6,7,8]], [2, 3]); // should return 8
var z = getValueFromArray(
[
[[1,2,3,4], [1,2,3,4], [1,2,3,4]],
[[1,2,3,4], [1,2,3,4]]
],
[0, 1, 2]
); // should return 3
Such call should return 3, and if I debug the function, it actually returns the correct value but when I assign it to a variable, it returns undefined. I guess it's because of the recursion, variable gets the value that is undefined during the first function call. How can this be fixed?
Thank you!
You are not returning your recursive results.
if(arr[currentIndex].length)
getValueFromArray(arr[currentIndex], indexes);
Should be:
if(arr[currentIndex].length)
return getValueFromArray(arr[currentIndex], indexes);
you missed one thing in your code(returning the result after if condition) try below given code:-
function getValueFromArray(arr, indexes){
var val,
currentIndex = indexes[0];
if(!arr[currentIndex] || arr[currentIndex] === '') return value = '';
indexes.splice(0, 1);
if(arr[currentIndex].length)
return getValueFromArray(arr[currentIndex], indexes);
else {
val = arr[currentIndex];
return val;
}
}
var y = getValueFromArray([[1,2,3,4], [1,2,3,4]], [0, 2]);
console.log(y)
run it and see, now its showing the result into the variable.
I think you need to add a return before
getValueFromArray(arr[currentIndex], indexes);
To make the final calculated value to recurse up the recursive method call stack as each recursed call returns.
Your guess is correct. You simply forgot to return whatever the recursive call returns:
if(arr[currentIndex].length)
getValueFromArray(arr[currentIndex], indexes); // <---- here
That being said, I have to agree that you could easily do it more concisely (this will eventually destroy indexes, however):
function getValueFromArray(arr, indexes){
while(indexes.length) arr=arr[indexes.shift()]
return arr
}
It's because if condition is not returning any value.
Try following code
function getValueFromArray(arr, indexes){
var val='',currentIndex = indexes[0];
if(!arr[currentIndex] || arr[currentIndex] === '') return val;
indexes.splice(0, 1);
if(arr[currentIndex].length) {
// Because if your function walks this way
// it does not meet any 'return' statement
// till the end and returns nothing.
return getValueFromArray(arr[currentIndex], indexes);
}
else {
val = arr[currentIndex];
return val;
}
}
Then console log you variable
var y = getValueFromArray([[1,2,3,4], [1,2,3,4]], [0, 2]);
console.log(y)
I'll post my code too as I feel it provides a simpler solution to your problem.
There is no recursion involved, so it should run a bit faster in theory.
var arr = [
[
[
[12, 5, 6],
[ 6, 7, 8],
[11, 0, 9]
],
[
[-1, 1, 8],
[ 4, 5, 6]
]
],
[
[
[7, 8, 9, 10]
]
]
];
function getValueFromArray(arr, indexes){
var value = arr, i = 0, len = indexes.length, index = null;
for (; i < len; i += 1) {
index = indexes[i];
// check if the current array contains an {index}-th record
if ( index in value ) {
value = value[index];
} else {
// or throw an exception if you want
return null;
}
}
return value;
}
getValueFromArray(arr, [0, 1, 1, 2]) // 6
function getValueFromArray(arr, indexes) {
// exit if arr is not an array
// exit if arr is empty
// exit if indexes is not an array
// exit if indexes is empty
if (!Array.isArray(arr) || !arr.length || !Array.isArray(indexes) || !indexes.length) {
return; // may throw exception or return other value
}
var currentIndex = indexes.shift();
// exit if index is not in arr
// exit if index is negative
if (arr.length <= currentIndex || currentIndex < 0) {
return; // may throw exception or return other value
}
return Array.isArray(arr[currentIndex]) ? getValueFromArray(arr[currentIndex], indexes) : arr[currentIndex];
}
You are overthinking this:
function getValueFromArray(arr, indexes){
return arr[indexes[0]][indexes[1]];
}
EDIT for 1 to 3 dimensional array:
function getValueFromArray(arr, indexes){
if (indexes.length == 1) {
return arr[indexes[0]];
} else if (indexes.length == 2) {
return arr[indexes[0][indexes[1]];
} else if (indexes.length == 3) {
return arr[indexes[0][indexes[1][indexes[2]];
} else {
// 4 dimensional arrays???
}
}
Would you have more than 3 dimensional array?
It would be best find a way to append indexes[i] but I can't think of a way at the moment and I don't think it's possible.