Relatively new at programming, looking for an explanation of a question in Chapter 5 of Eloquent Javascript.
Your own loop Write a higher-order function loop that provides something like a for loop statement. It takes a value, a test
function, an update function, and a body function. Each iteration, it
first runs the test function on the current loop value and stops if
that returns false. Then it calls the body function, giving it the
current value. Finally, it calls the update function to create a new
value and starts from the beginning.
When defining the function, you can use a regular loop to do the
actual looping.
loop(3, n => n > 0, n => n - 1, console.log); // → 3 // → 2 // → 1
The answer:
function loop(start, test, update, body) {
for (let value = start; test(value); value = update(value)) {
body(value);
}
}
loop(3, n => n > 0, n => n - 1, console.log);
// → 3
// → 2
// → 1
Looking for a walkthrough of what the code is doing. Any help to clearly understand this problem would be helpful.
A for loop consists of 4 parts:
Initialization - this is run at the beginning of the loop; it usually initializes the variable(s) used in the Condition and Repetition parts.
Condition - This is executed before each iteration. If its value is truthy, the body is then executed; otherwise, the loop stops immediately.
Repetition - This is executed after each body execution, and usually updates variable tested by the Condition.
Body - This is just ordinary code that's executed each time through the loop. It usually makes use of the variable(s) updated by the other parts.
The syntax is:
for (Initialization; Condition; Repetition) {
Body
}
So in your example, the Initialization sets the current loop value (the value variable) to the value that was given to the function.
The Condition then executes test(value) function. The for loop will automatically test whether this returns true or false -- if it returns false the loop stops. This fulfills the requirement:
Each iteration, it first runs the test function on the current loop value and stops if that returns false.
Then the Body is executed, and this executes body(value). This implements the requirement:
Then it calls the body function, giving it the current value.
Then the Repetition executes value = update(value), which implements:
Finally, it calls the update function to create a new value and starts from the beginning.
"For loops" are a built-in feature common to programming languages, including JavaScript. The Wikipedia article about them is pretty thorough, and I'd start there if you wanted to understand the context of the question.
The exercise at hand, then, is to write a function in JavaScript that roughly-speaking provides similar features to the built-in "for loop".
The solution you quoted defines a function ("loop") that accepts four parameters: an initial value ("start"); a function that runs at before each iteration, to test whether the loop should stop ("test"); a function that will be run after each iteration ("update"); and the function that should be run in the body of the loop during each iteration ("body"). It then uses an actual for loop to perform the requested iterations.
Trying to understand this myself while reading this...but from what I grasp from it it sounds like the author is trying to teach you one of two things.
That a for loop consists of a
for(initialExpression; condition; incrementExpression){
and then the statement
}
That you can create your own reusable customizable loop function that can function like forEach() or how ever you would like it to function.
Solving this with a for loop seems awkward, I understand why you are confused, just like me - I'm noob too :)
Here's a recursive solution, hopefully it will shed more light, if you haven't completely figured stuff out:
function loop(value, test, update, execute) {
if (!test(value)) return; // stop
execute(value);
return loop(update(value), test, update, execute);
}
Related
Assume I have some function f(-) defined elsewhere. I use it twice in the following type of code:
if (f(-) satisfies some condition) {
do some stuff
if (f(-) satisfies some other condition) {
do some other stuff
}
}
When the code is run, is f(-) calculated twice (or is the interpreter "intelligent" enough to see that it is enough to calculate it once)? If so, is it recommended to define a constant x = f(-) before that code and use that instead of f(-)? Usually I do this, but I am not fully sure if it is necessary, in particular, when f(-) can be computed really fast.
In case it matters, the language is JavaScript.
if (f(-) satisfies some other condition) will call the same function and it may never enter the block followed by this if, since the function satisfies the outer if that is why it entered into that block.
Create a const and depending on that value execute next step
const isFunResult = f(-);
// expecting this function gives a boolean
if(isFunResult){
// code goes here
}
else{
// code goes here
}
You can also use switch statement if function return multiple result .
In short words, the interpreter is not "intelligent" enough to see that it is enough to calculate it once. So it will call the function twice.
What you are looking for is something called memoization which is used to optimize performance for computational heavy functions to be remembered when the same parameters are passed.
If you need memoization, you could either implement that yourself in your own functions or you could use a javascript framework that supports that. Like React has the useMemo hook.
See https://reactjs.org/docs/hooks-reference.html#usememo
Usually you wont need that and just storing the result of the function in a variable will be enough for most use cases.
Run this to see the examples. Even if the conditions are the same.
function test_me() {
console.log("I got called");
return 99;
}
if (test_me() == 99) console.log("n=99"); // 1st function call
if (test_me() == 99) console.log("n=99"); // 2nd function call
// is different from the following
var n = test_me(); // function called once
if (n == 99) console.log("n=99"); // 1st test
if (n == 99) console.log("n=99"); // 1st test
To answer your question: Yes, it calculates function f(-) TWICE.
More than it is the intelligence of the interpreter, it is the intent of the programmer. The same function can be called twice if the programmer thinks that the function might return a changed value the second time. i.e: if the function uses a new Date() inside the function...
But, if the programmer knows that it is the same output both times (as in your case), it is better to define a constant x = f(-) and reuse it. This will improve performance (if the function is heavy), and improves code maintainability.
Even if it is a compiler, the compiler might not be intelligent enough to detect this, unless the function is a very simple code. But as #perellorodrigo mentioned, you can use memoization, but it is beyond the scope of this answer.
To wrap up, if you call the same function twice, it will be evaluated twice.
I'm new to programming and am unable to find any good explanation on parameter/arguments, how they work under the hood. For eg:
function changeStuff(a) {
return a = a * 10;
}
var num = 10;
console.log(changeStuff(num)); //prints 100
console.log(num); //prints 10
When I call this changeStuff function, how does javascript put variable num into parameter - a? Does it do something like a = num under the hood?
I'm sorry if its a bad or a dumb question.
You would need to see compiled code rather than this interpreted code to understand it more, as you don´t even deal with registers here.
Assuming it works same way as c++ (probably similar), when calling a function, function parameters are pushed onto current stack. Then the function reads values from the stack according to the amount and function reads/writes those values.
In compiled code, there will not exist such thing as 'a' variable. Only a limited amount of registers are available, so a will actually be one of those. Before assigning that, the value from the register will be pushed onto the stack, so that when function ends, the register can go back to its previous value for the running code that might have been using it before calling the function.
A bit of literature on the subject
Javascript copies the reference to the function a * 10 to the variable a in this case. So a is a * 10 then and so a * 10 will evaluated and returned.
I really did overflow the stack on my first try, but then I put the return statement (see comment in code) without an argument and it worked.
Task:
Write a higher-order function loop that provides something like a for loop statement. It takes a value, a test function, an update function, and a body function. Each iteration, it first runs the test function on the current loop value, and stops if that returns false. Then it calls the body function, giving it the current value. And finally, it calls the update function to create a new value, and starts from the beginning.
When defining the function, you can use a regular loop to do the actual looping.
I didn't use loop.
Book Solution
function loop(start, test, update, body) {
for (let value = start; test(value); value = update(value)) {
body(value);
}
}
loop(3, n => n > 0, n => n - 1, console.log);
// → 3
// → 2
// → 1
My Solution (kept my original function parameter names)
function loop(value, test, update, execute){
if (test(value)) execute(value);
else return // prevents stack overflow?
return loop(update(value),test,update,execute)
}
loop(3, n => n > 0, n => n - 1, console.log);
// → 3
// → 2
// → 1
Did I just made the console output the same thing, or will my solution do the same thing in a real-environment program?
I ask, because I'm not sure if I actually solved it with recursion, or just made the console output the same thing. This will help me feel better about myself, since I'm a noob JS learner. Thanks! :)
Yes, that's the correct recursive implementation for the task. You didn't just hit the correct output by chance.
The else return is a bit odd though. I would have written either
function loop(value, test, update, execute) {
if (test(value)) {
execute(value);
return loop(update(value), test, update, execute);
} // else stop
}
or
function loop(value, test, update, execute) {
if (!test(value)) return; // stop
execute(value);
return loop(update(value), test, update, execute);
}
Initializer
The book solution creates a value variable and initializes it with start. In your case this happens automatically when you pass your first parameter. So far the two approaches are equivalent.
Test
The for loop calls test(value) as cycle test. Your approach does something similar, but calling test is the test for recursion. So far the two approaches are equivalent.
Cycle block
The for loop calls body(value). Your approach calls execute(value). So far the two approaches are equivalent.
The update
value is updated in the cycle after each iteration. The same happens in your code when you pass update(value) in your recursive call. So far the two approaches are equivalent.
Are they really equivalent?
Algorithmically, yes. Technically, no. Your recursive approach calls the function several times and uses the stack (of the memory) to store the function calls. In the case of a too large numbers of test, your code will crash. So, you have successfully implemented a recursive version of the book example (yay!) but you should try to avoid recursion in most cases.
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 have seen some developers place a return at the end of their JavaScript functions like this:
$(".items").each(function(){
mthis = $(this);
var xposition = some .x() position value;
if(xposition < 0){
mthis.remove();
return;
}
});
Is having a return even necessary? I know that return false cancels out of a loop early and I know that return x, returns a value, but having just return??? What does that mean?
Sorry - I forgot to put an end } at the very end of the code. the return is in the if conditional.
New Update - just discovered that the intent of the loop was to cancel out the nth .item that entered the loop. so return false is my replacement for a simple return; (which means undefined anyway). Thanks everyone for the help!
Having return with not proceeding it at the end of the function doesn't really do anything. Returning "this" however allows for method chaining.
Your example is specific for how jQuery.each handles the return values from a loop function. From the docs:
We can break the $.each() loop at a particular iteration by making the
callback function return false. Returning non-false is the same as a
continue statement in a for loop; it will skip immediately to the next
iteration.
So there you have it, returning anything else than false makes no difference, it will just move on to the next iteration. false, however, breaks the loop.
Note that return; is exactly the same as return undefined;.
It's just a way to end the function execution without necessarily having to choose a proper return value. At the end of the function it really doesn't make a difference.
The behaviour of return; anywhere in your function is exactly the same as when the code reaches the end of your function, the caller receives undefined.
In that code it's used together with a condition. I would have to assume that you didn't show the full code.
This method is used often to jump to the end of the function when a pre-condition is not met as it might be more readable than five indentations, each with another condition.
The problem is that your opening and closing { } braces don't line up, so depending on which is correct, this will perform differently. If the closing brace for the if is after the return, then this will act like a continue in a regular for loop.
$(".items").each(function(){
mthis = $(this);
var xposition = some .x() position value;
if (xposition < 0) {
mthis.remove();
return;
}
}
As it turns out, a continue at the end of a for loop is also implicit, so this doesn't really serve a purpose.
Of course, if you're question is more theoretical, the answer is that generally it doesn't make a difference:
function doSomething() {
alert("Hello, world");
return; //purely a matter of preference
}
Here, the method would implicitly return even without the direct invocation of return by virtue of being at the end of the code-block; in some cases it can be useful just as an indicator that you are at the end of the function, particular if there was a lot of nesting. If not at the bottom, it definitely makes a difference, since nothing after the return; will be executed. Otherwise the difference is purely stylistic. Saves you a few characters to leave it out of course!
Just like return false, it'll cancel out of the block (in this case, the function) early. The return value will be undefined.
At the end of a function, it's equivalent to not using a return statement at all, but your example doesn't look like the end of a function to me.
EDIT: Just to make it a bit clearer: This particular return statement is at the end of the surrounding if clause, not the function as a whole. In other words, the condition of the if clause specifies a situation in which the function should terminate mid-way.
EDIT 2: By "the function", I refer to the inner, anonymous function. return;, being equivalent to return undefined;, will indeed cause jQuery.each() to move on to the next item. (Also, it seems that I was mistaken about there being more code in the function; given the OP's edit to the code sample, it really does look like a pointless return statement.)