Considering the following recursive Fibonacci function that’s been optimized using memoization.
No other code apart from this.
function memoizeFibonacci(index, cache = []) {
if (cache[index]) {
return cache[index]
} else {
if (index < 3) return 1
else {
cache[index] = memoizeFibonacci(index - 1, cache) + memoizeFibonacci(index - 2, cache)
}
}
console.log(cache)
return cache[index];
}
memoizeFibonacci(6)
Can someone please explain how is the cache array updated? When viewing the console logs the cache seems to hold all the previous values from the resolved recursive functions. But to me, this doesn't make sense as the cache is not stored outside memoizeFibonacci so the scope should not allow this.
Every nested recursive function adds its item when it resolves only during the chain of executions of the recursion.
console.log is asynchronous and probably shows repeatedly the final result. There is no specification about how console.log work so it can act differently depending on the environment, it's I/O.
Even if you make it work as expected in your environment, it can work differently for other user. An hypothesis based on the use of console in your algorithm is not correct.
Kyle Simpson references:
Recursion in JS https://github.com/getify/Functional-Light-JS/blob/master/manuscript/ch8.md
Console log https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/async%20%26%20performance/ch1.md#async-console
This has nothing to do with closures. This is simply passing the same array to nested recursive calls.
When you pass an array (or any object for that matter) to a function, the array is not copied, rather a reference to it is passed, so changing the array in the recursive call will affect the same array.
Here is an example of what is basically happening:
function foo(arr) {
arr[0] = "hello";
}
let arr = [];
foo(arr);
console.log(arr); // changed because of the call to foo
Notice that the recursive calls to memoizeFibonacci is explicitly passing the cache object as the second parameter, so each recursive call is sharing the same array as the top-level call, and any changes to the cache object in the recursive calls is reflected in the top-level call aswell.
BTW, this type of memoization is not persistent, meaning that these two calls:
memoizeFibonacci(6);
memoizeFibonacci(10);
don't share the same cache object. They each use a different cache array that must be reconstructed from scratch rather than the call to memoizeFibonacci(10) using the cache object already constructed in the call to memoizeFibonacci(6) and appending to it. A more efficient memoization makes use of closures like in this example: https://stackoverflow.com/a/8548823/9867451
Note: If you are asking why all the console.log(cache) print out the same exact filled array, that's because they are printing the same array, and the values you see in the console are not necessarily added at the point of the console.log. Take a look at this other question: array.length is zero, but the array has elements in it. To log exactly what's in the cache object at the time of the console.log, change it to:
console.log(JSON.stringify(cache));
Related
In the last year I've been using array methods like map and filter more often instead of the standard for loop on an array. It feels simpler to read and write, and does all the things I'm most likely going to do anyway, like create a local variable.
Often times I don't return anything though. Eslint doesn't like me very much though. According to them, they say you always need a return, otherwise its "probably a mistake"
https://eslint.org/docs/rules/array-callback-return
Why? Is just good practice? What are downsides of a return-less array method?
Been thinking on this for a while. Any insight or thoughts would be great.
Should I use array methods like map and filter, if I'm not going to return anything?
No, you should not.
Why? Is just good practice?
Yes. It is a good practice to use the appropriate iteration method for the type of iteration you are doing. There are numerous ways to iterate for a reason. Pick the appropriate mechanism.
What are downsides of a return-less array method?
Using .map() and .filter() without actually returning anything from the callback have the following downsides:
Your code is misleading. The point of .map() and .filter() is to iterate over the array and produce a new array. When a developer reads some code and sees .map() or .filter() being used, they expect that there should be a returned array. When they don't see it being done that way, they will be confused, will initially feel like they don't understand the code. If I were doing a code review on code like this, I would not approve of code like this.
Your code unnecessarily creates objects that are not used. That's just wasteful and is not a good practice. Instead, use an iteration method that does not produce an output array such as for/of, a regular for loop or .forEach().
Your code won't lint. Linters provide objections to things for a reason. Using .map() or .filter() without returning anything from the callback is, just as the linter says, "probably a programming mistake" because that is not how those functions are designed to be used and there are appropriate alternatives when you don't want a returned array.
So, if you're just trying to do an iteration without creating any resulting array, use for/of or .forEach() or some other iteration scheme that isn't specifically designed to create an output array that you don't want.
First you need to know about the difference between Map/Filter and forEach.
resuming.. forEach is mostly used when you want iterate an array with/as a procedure. check
Map and Filter are related to a callback function applied on every iteration.
The return statement of those is what is going to be evaluated, not the function By the Map/Filter function at the end. Reason why it's needed. Althought JS allows "whatever" And of course you are able to define that function what comes to our understanding as "The filter".
For Filter you can see that "true" and "false" as when the "data" is going to be filtered or not.
basically you can loop with map or forEach/for, the difference are the following:
foreach: This iterates over a list and applies some operation with side effects to each list member, this means that you are transforming THE CURRENT ARRAY you are looping.... or as noticed by #TiagoCoelho, you dont mess with the array at all, just loop thought it.
map: This iterates over a list, transforms each member of that list, and returns another list of the same size with the transformed members, this means that you will get a BRAND NEW ARRAY with the modified items and you will also have in memory your old array.
so basically it depends on what you want to do with the data inside of your array.
References
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
examples:
var a = [1, 2, 3, 4];
var b = [1, 2, 3, 4];
//multiply each item for 2, it will stay on the same array.
a.forEach(i => {
i = i * 2
})
//multiply the items of B for 2 but it will return a new array
var c = b.map(i => {
return i * 2
})
console.log(a); //modified reference
console.log(b); //stays the same
console.log(c); //new array
I want to modify each object in an array, as well as remove duplicates. I am already using lodash's uniqWith to compare items so I figured I would do some other logic within the comparator function to modify each item so I can avoid setting up another loop. Are there any problems with having extra logic within the comparator like below?
import uniqWith from 'lodash/uniqWith';
const transformedArray = uniqWith(
arrayOfObjects,
(currObject, otherObject): boolean => {
// modifying current object's properties, is this legit??
if (<someCondition>) {
currObject.someProperty = true;
}
// actual comparison logic
if (currObject.uuid === otherObject.uuid) {
return true;
}
return false;
},
);
The uniqWith documentation only says the following about the callback invocation:
The comparator is invoked with two arguments: (arrVal, othVal).
I personally wouldn't use the uniqWith compare function, for iteration work. The documentation doesn't disclose how often or in what order the compare function will be called. Say you have the array [1,2,3]. I would assume each element is compared against all elements in the output, to check for uniqueness. The implementation might look something like:
function uniqWith(array, compareFn) {
const unique = [];
for (const current of array) {
const isDuplicate = unique.some(other => compareFn(current, other));
if (!isDuplicate) unique.push(current);
}
return unique;
}
If this is indeed the case that means the callback is never called for 1, once for 2, and twice for 3.
Generally speaking don't use callback functions to do iteration work, unless the function that accepts the callback discloses when/how often/what order etc. this function is called.
Take for example the Array forEach method. The documentation discloses all these things.
forEach() calls a provided callback function once for each element in an array in ascending order. It is not invoked for index properties that have been deleted or are uninitialized. (For sparse arrays, see example below.)
Short answer: no, if by "wrong" you mean a breaking issue.
Medium Answer: It shouldn't interfere with the method. Just be aware that doing mutations in unique locations, where you otherwise may not expect them, is trading clarity for brevity- which can lead to confusion and debugging nightmares later down the road if you lose track of them.
After more thorough testing I realized that there is some optimization done in lodash/uniqWith where not every item is necessarily passed to the comparator function as the currObject. So as far as my example code in the original question, it is not guaranteed that every item in array will receive the .someProperty = true. Should've tested this more before posting, but still appreciate the feedback.
var f_drum_min = function myself(a){
alert(a);
$f_min_node.push(a);
for (i=0;i<=$m;i++){
if ($f_leg[i][1]==a){
myself($f_leg[i][0]);
}
}
};
myself($f_leg[i][0]); breaks the for loop , how can I make it run multiple times in loop?
Your function is riddled with bad habits
There's no way for me to improve this function because I have no idea what all of those external states do. Nor is it immediately apparent what their data types are.
These are bad habits because there's no possible way to know the effect of your function. Its only input is a, yet the function depends on $f_min_node, $f_leg, and $m.
What is the value of those variables at the time you call your function?
What other functions change those values?
I assigned $f_min_node to some value and then called f_drum_min. How was I supposed to know that $f_min_node was going to get changed?
Every time you call your function, it's a big surprise what happens as a result. These are the woes of writing non-deterministic ("impure") functions.
Until you can fix these problems, recursion in a for-loop the least of your concerns
I have annotated your code with comments here
// bad function naming. what??
var f_drum_min = function myself(a){
// side effect
alert(a);
// external state: $f_min_node
// mutation: $f_min_node
$f_min_node.push(a);
// leaked global: i
// external state: $m
for (i=0;i<=$m;i++){
// external state: $f_leg
// loose equality operator: ==
if ($f_leg[i][1]==a){
myself($f_leg[i][0]);
}
}
};
I can help you write a deterministic recursive function that uses a linear iterative process though. Most importantly, it doesn't depend on any external state and it never mutates an input.
// Number -> Number
var fibonacci = function(n) {
function iter(i, a, b) {
if (i === 0)
return a;
else
return iter(i-1, b, a+b);
}
return iter(n, 0, 1);
}
fibonacci(6); // 8
for loops are pretty primitive; Imperative programmers will reach for it almost immediately thinking it's the only way to solve an iteration problem.
I could've used a for loop in this function, but thinking about the problem in a different way allows me to express it differently and avoid the for loop altogether.
One basic problem with the code, which would cause it to break under almost any circumstances, is that the loop variable i is a global, and is thus shared by all recursive invocations of the function.
For example, let's say the function is invoked for the first time. i is 0. Now it recurses, and let's say that the condition in the if is never true. At the end of the 2nd call, i = $m + 1. When you return to the first call, because i is global, the loop in the first call ends. I assume this is not what you want.
The fix for this is to declare i as local:
for (var i=0;i<=$m;i++){
This may or may not fix all of your problems (as pointed out in comments, we'd have to see more of your code to identify all possible issues), but it is a critical first step.
I was reading the source code for pallet.js and came across this.
var ret = (function(proto) {
return {
slice: function(arr, opt_begin, opt_end) {
return proto.slice.apply(arr, proto.slice.call(arguments, 1));
},
extend: function(arr, arr2) {
proto.push.apply(arr, arr2);
}
};
})(Array.prototype);
var slice = ret.slice;
var extend = ret.extend;
Why is this necessary? Why could they not simply write this:
var slice = function(arr,opt_begin,opt_end) {
return Array.prototype.slice.apply(arr,[opt_begin,opt_end]));
}
var extend = function(arr,arr2) {
return Array.prototype.push.apply(arr,arr2);
}
EDIT 1:
In response to the duplicate question. I don't think it is a duplicate, but that question definitely does address my question. So it is an optimization. But won't each one only be evaluated once? So is there really a significant improvement here for two function calls?
Also if we are worried about performance why are we calling proto.slice.call(arguments,1) instead of constructing the array of two elements by hand [opt_begin,opt_end], is slice faster?
Because the syntax is just so much cooler. Plus you can rationalize it's use by telling yourself that it's more DRY. You didn't have to type Array.prototype twice.
I can't be sure what was the original rationale behind that code (only the author knows) but I can see a few differences:
proto is a closed-over local variable, while instead Array is a global. It's possible for a smart enough Javascript engine to optimize access because proto is never changed and thus it could even be captured by value, not reference. proto.slice can be faster than Array.prototype.slice because one lookup less is needed.
passing opt_begin and opt_end as undefined is not the same as not passing them in general. The called function can know if a parameter was passed and happens to be undefined or if instead it wasn't passed. Using proto.slice.call(arguments, 1) ensures that the parameters are passed to slice only if they were actually passed to the closure.
I'm currently reading through this jquery masking plugin to try and understand how it works, and in numerous places the author calls the slice() function passing no arguments to it. For instance here the _buffer variable is slice()d, and _buffer.slice() and _buffer seem to hold the same values.
Is there any reason for doing this, or is the author just making the code more complicated than it should be?
//functionality fn
function unmaskedvalue($input, skipDatepickerCheck) {
var input = $input[0];
if (tests && (skipDatepickerCheck === true || !$input.hasClass('hasDatepicker'))) {
var buffer = _buffer.slice();
checkVal(input, buffer);
return $.map(buffer, function(element, index) {
return isMask(index) && element != getBufferElement(_buffer.slice(), index) ? element : null; }).join('');
}
else {
return input._valueGet();
}
}
The .slice() method makes a (shallow) copy of an array, and takes parameters to indicate which subset of the source array to copy. Calling it with no arguments just copies the entire array. That is:
_buffer.slice();
// is equivalent to
_buffer.slice(0);
// also equivalent to
_buffer.slice(0, _buffer.length);
EDIT: Isn't the start index mandatory? Yes. And no. Sort of. JavaScript references (like MDN) usually say that .slice() requires at least one argument, the start index. Calling .slice() with no arguments is like saying .slice(undefined). In the ECMAScript Language Spec, step 5 in the .slice() algorithm says "Let relativeStart be ToInteger(start)". If you look at the algorithm for the abstract operation ToInteger(), which in turn uses ToNumber(), you'll see that it ends up converting undefined to 0.
Still, in my own code I would always say .slice(0), not .slice() - to me it seems neater.
array.slice() = array shallow copy and is a shorter form of array.slice()
Is there any reason for doing this, or is the author just making the code more complicated than it should be?
Yes there may be a reason in the following cases (for which we do not have a clue, on whether they apply, in the provided code):
checkVal() or getBufferElement() modify the content of the arrays passed to them (as second and first argument respectively). In this case the code author wants to prevent the global variable _buffer's content from being modified when calling unmaskedvalue().
The function passed to $.map runs asynchronously. In this case the code author wants to make sure that the passed callback will access the array content as it was during unmaskedvalue() execution (e.g. Another event handler could modify _buffer content after unmaskedvalue() execution and before $.map's callback execution).
If none of the above is the case then, yes, the code would equally work without using .slice(). In this case maybe the code author wants to play safe and avoid bugs from future code changes that would result in unforeseen _buffer content modifications.
Note:
When saying: "prevent the global variable _buffer's content from being modified" it means to achieve the following:
_buffer[0].someProp = "new value" would reflect in the copied array.
_buffer[0] = "new value" would not reflect in the copied array.
(For preventing changes also in the first bullet above, array deep clone can be used, but this is out of the discussed context)
Note 2:
In ES6
var buffer = _buffer.slice();
can also be written as
var buffer = [..._buffer];