So I'm looking at a piece of code that is confusing me despite reading an explanation or two:
Here's the code..
var puzzlers = [
function (a) { return 8*a - 10; },
function (a) { return (a-3) * (a-3) * (a-3); },
function (a) { return a * a + 4; },
function (a) { return a % 5; }
];
var start = 2;
var applyAndEmpty = function (input, queue) {
var length = queue.length;
for (var i = 0; i < length; i++) {
input = queue.shift()(input);
}
return input;
};
alert(applyAndEmpty(start, puzzlers));
I understand most of it but a breakdown would be great, what really boggles me is the beginning and end of this line input = queue.shift()(input); I know it's using input to store the outcome, but why? and why is there an input parameter on the end as well?
PS This line alert(applyAndEmpty(start, puzzlers)); I know calls the function then alerts it. Why do I have to call a function before I can alert/console log etc it? Is this because it isn't IIFE and so theres nothing to actually alert until the function has been called? Its quite like an 'on' button?
Sorry this is long, thanks in advance!
I've slightly edited the code in the for loop for clarity.
// This array contains 5 items, each item is a function which takes a single param and returns a number.
var puzzlers = [
function (a) { return 8*a - 10; },
function (a) { return (a-3) * (a-3) * (a-3); },
function (a) { return a * a + 4; },
function (a) { return a % 5; }
];
// The start value is 2.
var start = 2;
var applyAndEmpty = function (input, queue) {
// Get the number of items in the queue.
var length = queue.length;
// Iterate over all the items in the queue.
for (var i = 0; i < length; i++) {
// Remove the item at index 0 from the queue, the item is stored in the var.
var itemMethod = queue.shift();
// Execute the method, pass it the current value as input param. The result
// of the method will be placed back into the input variable.
input = itemMethod(input);
}
// Return the modified input value.
return input;
};
// Runs the applyAndEmpty method and shows the output in an alert.
alert(applyAndEmpty(start, puzzlers));
// The breakdown of the for loop:
// i = 0, input = 2 -> return 8 * 2 - 10 = 6
// i = 1, input = 6 -> return (6-3) * (6-3) * (6-3) = 27
// i = 2, input = 27 -> return 27 * 27 + 4 = 733
// i = 3, input = 733 -> return 733 % 5 = 3
// And thus the alert says three.
If you don't place the result of the current itemMethod back into input it means you will call each method from puzzlers with the value 2. The result of applyAndEmpty would no longer be 3 but just be 2 as the input variable is never changed. So if you don't store the result of calling the puzzler methods you might as well skip them altogether and just return the input param immediately.
It's just a way to chain the functions in the array, so that the result from the first function is passed to the second function, the result from the second function is passed to the third function, and so on...
f = [ a => 8 * a - 10,
a => (a-3) * (a-3) * (a-3),
a => a * a + 4,
a => a % 5 ]
console.log(f[3](f[2](f[1](f[0](2))))) // 3
console.log(f.pop()(f.pop()(f.pop()(f.pop()(2))))) // 3
console.log((a => a % 5)((a => a * a + 4)((a => (a-3) * (a-3) * (a-3))((a => 8*a - 10)(2))))) // 3
Related
I've been trying to solve the following problem in codewars using recursion:
Write a function, persistence, that takes in a positive parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num until you reach a single digit.
For example (Input --> Output):
39 --> 3 (because 3*9 = 27, 2*7 = 14, 1*4 = 4 and 4 has only one digit)
999 --> 4 (because 9*9*9 = 729, 7*2*9 = 126, 1*2*6 = 12, and finally 1*2 = 2)
4 --> 0 (because 4 is already a one-digit number)
Here's what I've tried:
var numOfIterations = 0;
function persistence(num) {
//code me
var i;
var digits=[];
var result = 1;
if (num.toString().length==1) {
return numOfIterations;
} else {
numOfIterations++;
digits = Array.from(String(num), Number);
for (i=0;i<digits.size;i++) {
result=result*digits[i];
}
persistence(result);
}
}
But for some reason, instead of returning the number of iterations, it returns undefined. I've been told that I'm not using recursion correctly, but I just can't find the problem.
Other answers have explained what's wrong with your code. I just want to point out a simpler implementation:
const multiplyDigits = (n) =>
n < 10 ? n : (n % 10) * multiplyDigits (n / 10 | 0);
const persistence = (n) =>
n < 10 ? 0 : 1 + persistence (multiplyDigits (n));
[39, 999, 4] .forEach (t => console .log (`${t}:\t${persistence (t)}`));
multiplyDigits does just what it says, recursively multiplying the final digit by the number left when you remove that last digit (Think of | 0 as like Math .floor), and stopping when n is a single digit.
persistence checks to see if we're already a single digit, and if so, returns zero. If not, we add one to the value we get when we recur on the multiple of the digits.
I've been told that I'm not using recursion correctly
You're recursing, but you're not returning the result of that recursion. Imagine for a moment just this structure:
function someFunc() {
if (someCondition) {
return 1;
} else {
anotherFunc();
}
}
If someCondition is false, what does someFunc() return? Nothing. So it's result is undefined.
Regardless of any recursion, at its simplest if you want to return a result from a function then you need to return it:
function persistence(num) {
//...
if (num.toString().length==1) {
//...
} else {
//...
return persistence(result); // <--- here
}
}
As #David wrote in his answer, you were missing the return of the recursive call to itself.
Plus you were using digits.size instead of digits.length.
Anyway consider that one single digit being zero will collpse the game because that's enough to set the result to zero despite how many digits the number is made of.
To deal with the reset of numOfIterations, at first I tried using function.caller to discriminate between recursive call and direct call and set the variable accordingly. Since that method is deprecated as shown here:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller
I opted for the optional argument iteration that gets set to zero as default, to keep track of that value while it goes down the call stack. This solution still fulfills the fact that the caller doesn't need to know a new interface for the function to work.
//var numOfIterations = 0;
function persistence(num, iteration=0) {
/*
Commented strategy using the function.caller
working but deprecated so I can't recommend anymore
used optional argument iteration instead
//gets the name of the caller scope
let callerName = persistence.caller?.name;
//if it's different from the name of this function
if (callerName !== 'persistence')
//reset the numOfIterations
numOfIterations = 0;
*/
var digits=[];
if (num.toString().length==1){
return iteration;
} else {
var result = 1;
digits = Array.from(String(num), Number);
for (let i=0;i<digits.length;i++) {
result = result * digits[i];
}
return persistence(result, iteration+1);
}
}
console.log( persistence(39) ); //-> 3
console.log( persistence(999 ) ); //-> 4
console.log( persistence(4) ); //-> 0
You can do something like this
const persistenceTailRecursive = (num, iterations = 0) => {
const str = '' + num;
if(str.length === 1){
return iterations;
}
return persistenceTailRecursive(str.split('').reduce((res, a) => res * parseInt(a), 1), iterations + 1)
}
const persistence = (num) => {
const str = '' + num;
if(str.length === 1){
return 0;
}
return 1 + persistence(str.split('').reduce((res, a) => res * parseInt(a), 1))
}
console.log(persistenceTailRecursive(93))
console.log(persistenceTailRecursive(999))
console.log(persistence(93))
console.log(persistence(999))
There are 2 versions
1 tailRecursive call the same method with the exact signature (preventing stackoverflow in some languages like scala)
2 basic the result is calculated at the end
I am doing an exercise with linked lists in Javascript. As part of my solution, I have a function that takes a linked list of one digit integers and converts it to a single number. The value being returned is not the same as the value logged to the console the line before the return.
for example, if I input (2 -> 4 -> 3), my console.log is 342, just as it should, but the value returned in otherFunction is 386. Input (5 -> 6 -> 4) logs 465, but returns 535.
How is the value of num changing?
function parseListNumber(list, num = 0, multiplier = 1) {
digitValue = list.val * multiplier;
multiplier *= 10;
num += digitValue;
if (list.next) {
return num + parseListNumber(list.next, num, multiplier);
} else {
console.log(num) // num is correct here
return num;
}
}
function otherFunction(list) {
parseListNumber(list); // returned value is now somehow larger
};
You're adding num to your recursive call each time you enter your if statement. If you remove this, then when you hit your base case, your value will be carried back up through the previous recursive calls and remain unchanged.
See example below:
const list = {
val: 2,
next: {
val: 4,
next: {
val: 3
}
}
};
function parseListNumber(list, num = 0, multiplier = 1) {
let digitValue = list.val * multiplier;
multiplier *= 10;
num += digitValue;
if (list.next) {
return parseListNumber(list.next, num, multiplier); // remove `num +` (use tail recursion)
} else {
console.log(num) // num is correct here
return num;
}
}
function otherFunction(list) {
console.log("Actual res: ", parseListNumber(list));
};
otherFunction(list);
I'm trying to make some coordinate plane functions on React native. But I'm having a problem that I don't know how to get the next element on my array.
This is my array:
[
{"M":["0","0"]},
{"H":"100"},
{"V":"0"},
{"H":"100"},
{"V":"100"},
{"H":"0"},
{"V":"100"},
{"H":"0"},
{"V":"0"},
]
This is my function:
const rotate = (pathArray, angle) =>{
if (angle > 0){
let vCordinate;
return pathArray.map((cordinate)=>{
if(Object.entries(cordinate)[0][0] == "M"){
let mCordinate = Object.entries(cordinate)[0][1];
mCordinate[0] = (parseInt(mCordinate[0]) * Math.cos(angle)) - (parseInt(mCordinate[1]) * Math.sin(angle));
mCordinate[1] = (parseInt(mCordinate[1]) * Math.cos(angle)) + (parseInt(mCordinate[0]) * Math.sin(angle));
return {[Object.entries(cordinate)[0][0]]: mCordinate};
}
//LOGIC TO GET NEXT ELEMENT
if(Object.entries(cordinate)[0][0] == "H"){
let hCordinate = Object.entries(cordinate)[0][1];
vCordinate = Object.entries(cordinate)[0][1]
return {[Object.entries(cordinate)[0][0]]: vCordinate};
}
if(Object.entries(cordinate)[0][0] == "V"){
return {[Object.entries(cordinate)[0][0]]: vCordinate};
}
})
}
return pathArray;
}
In this point I need to get the next element when I've hit "H".
How to do this?
The callback of map method accepts 3 arguments:
current item value;
current item index;
the array map was called upon.
So, you could use index to get next element value:
var newArray = myArray.map(function(value, index, elements) {
var next = elements[index+1];
// do something
});
or
var newArray = myArray.map(function(value, index) {
var next = myArray[index+1];
// do something
});
Note, the value of next variable may be undefined. Test the variable, if need, to prevent errors.
I'm doing some javascript challenges on codeschool.com.
Whilst at the function expressions chapter, I came across the following weird bug.
In the following script - which can be heavily refactored, I know - the 'queue' variable gets assigned a value of 'undefined' after the second run.
First iteration:
queue array printed, containing 4 elements
"hello 1" printed
Second iteration:
queue array printed, containing 3 elements
"hello 3" printed
queue array printed, containing 2 elements
Third iteration - where the bug(?) occurs:
undefined is printed - instead of the queue array which should still contain 2 elements
First I thought something was wrong at codeschool's end, but after trying to run the code in chrome's dev tools I got the same results.
As far as I can tell nothing asynchronous should be happening which could mess anything up?
var puzzlers = [
function(a) { return 8 * a - 10; },
function(a) { return (a - 3) * (a - 3) * (a - 3); },
function(a) { return a * a + 4; },
function(a) { return a % 5; }
];
var start = 2;
var applyAndEmpty = function(input, queue) {
for (var i = 0; i < queue.length; i++) {
alert(queue);
if (i === 0) {
alert("hello 1");
var output = queue.shift()(input);
} else if (queue.length === 1) {
alert("hello 2");
return queue.shift()(output);
} else {
alert("hello 3");
output = queue.shift()(output);
alert(queue);
}
}
};
alert(applyAndEmpty(start, puzzlers));
Thanks!
Code Review
There's a couple bad things happening in this code; we should go over them first. For convenience's sake, I'm going to replace all alert calls with console.log calls – The console is much nicer for debugging things.
OK the first problem is you're using queue.shift() inside a for loop. Array.prototype.shift will change the length of the array, so you're not meant to use this inside a loop (outside of a very specialized case).
So each time we loop, i goes up one and queue.length goes down one – the two values are converging on each other which means you'll never actually touch all the values in queue
Refactor
We can fix that with a very simple adjustment of your function – remove the for loop! queue.shift() is effectively incrementing for us, but removing one element at a time. All we need to do is check if queue.length is empty – if so, we're done, otherwise shift one item off the queue and repeat
var applyAndEmpty = function(input, queue) {
console.log(input)
if (queue.length === 0)
return input;
else
return applyAndEmpty(queue.shift()(input), queue)
}
var puzzlers = [
function(a) { return 8 * a - 10; },
function(a) { return (a - 3) * (a - 3) * (a - 3); },
function(a) { return a * a + 4; },
function(a) { return a % 5; }
];
var start = 2;
console.log(applyAndEmpty(start, puzzlers));
// [initial input] 2
// 8 * 2 - 10 = 6
// (6 - 3) * (6 - 3) * (6 - 3) = 27
// 27 * 27 + 4 = 733
// 733 % 5 = 3
// [final output] = 3
console.log(puzzlers); // []
Greater lesson
There's one important thing to know about stepping thru arrays manually. You do ONE of the options below
1. check that the array is empty before attempting to get a value
// there are no items so we cannot get one
if (arr.length === 0)
doSomething;
// we know there is at least one item, so it is safe to get one
else
doOtherThing( arr.shift() )
2. null-check the thing you attempted to get
x = queue.shift();
// we didn't get a value, queue must've been empty
if (x === undefined)
doSomething
// yay we got something, we can use x now
else
doOtherThing( x )
It's your personal choice to use one over the other, tho I generally dislike null-checks of this kind. I believe it's better to check that the array has the value before attempting to grab it.
Doing BOTH of these options would be a logical error. If we check the array has a length of non-0, we can deduce we have an x – therefore we don't have to null-check for an x.
Remarks
I don't know if this was for an assignment or whatever, but you basically wrote up function composition from scratch – tho this is a kind of special destructive composition.
It seems you're aware of the destructive nature because you've named your function applyAndEmpty, but just in case you didn't know that was necessary, I'll share a non-destructive example below.
var lcompose = function(fs) {
return function (x) {
if (fs.length === 0)
return x
else
return lcompose(fs.slice(1))(fs[0](x))
}
}
var puzzlers = [
function(a) { return 8 * a - 10; },
function(a) { return (a - 3) * (a - 3) * (a - 3); },
function(a) { return a * a + 4; },
function(a) { return a % 5; }
];
var start = 2;
console.log(lcompose(puzzlers)(start)) // 3
That undefined you're getting is coming from this alert() call:
alert(applyAndEmpty(start, puzzlers));
At the end of the second iteration, i will be 1 and the queue length will be 2. Thus i is incremented for the next iteration, and the loop condition no longer holds — i is 2, and no longer less than the queue length.
It's a good habit when debugging to include some messaging instead of just values so that you can tell one debug output line from another. It's also a good idea to use console.log() instead of alert().
Assume the below solves your problem.
You used queue.length in second condition, at that stage, you perform only one shift and expecting a 4 size queue to be of 2 size, it is one error.
var puzzlers = [
function(a) { return 8 * a - 10; },
function(a) { return (a - 3) * (a - 3) * (a - 3); },
function(a) { return a * a + 4; },
function(a) { return a % 5; }
];
var start = 2;
var applyAndEmpty = function(input, queue) {
for (var i = 0; i < queue.length; i++) {
if (i === 0) {
alert("hello 1");
var output = queue.shift()(input);
} else if (i === 1) {
alert("hello 2");
return queue.shift()(output);
} else {
alert("hello 3");
output = queue.shift()(output);
}
}
};
alert(applyAndEmpty(start, puzzlers));
I want to create a function (reduce) that does the following:
Where:
var collection = [1, 2, 3];
and
function iterator(total, element) {
return total + element;
};
if initial is defined as 3:
reduce(collection, iterator, 3)
will do this:
3 + 1
4 + 2
6 + 3 = 9
if initial is undefined:
reduce(collection, iterator)
will do this:
1 + 2
3 + 3 = 6
Here is my code:
var reduce = function(collection, iterator, initial) {
if (initial === undefined) {
var total = 0;
} else {
var total = initial;
}
each(collection, function(element) {
total = iterator(total, element);
});
return total;
}
It works, but you can see that I've hard-coded total = 0, but I want this code to work in other scenarios (for example, multiplication, where I wouldn't want 0 to make the whole product 0).
This is how I would implement it:
alert(reduce([1,2,3], add, 3)); // 9
alert(reduce([1,2,3], add)); // 6
function add(a, b) {
return a + b;
}
function reduce(array, iterator, initial) {
var length = array.length;
var index = 0;
if (arguments.length < 3) {
if (length > 0) var result = array[index++]; // Note 1
else throw new Error("Reduce of empty array with no initial value");
} else var result = initial;
while (index < length) result = iterator(result, array[index++]);
return result;
}
The code is pretty self explanatory. Nevertheless, here's how it works, if the number of arguments passed are less than 3 it means that initial was not given. Hence we set result to array[0] and increment index. If array is empty then we throw an error instead. Otherwise we set the result to the initial value passed to the function. Everything else works as normal.
Note 1: The reason we don't modify initial (i.e. write initial = array[index++]) is because if we use arguments in a function and also modify the parameters of the function then the function will not be optimized in V8. Hence, it will execute slower.
Hope this helps.