What does "!--" do in JavaScript? - javascript

I have this piece of code (taken from this question):
var walk = function(dir, done) {
var results = [];
fs.readdir(dir, function(err, list) {
if (err)
return done(err);
var pending = list.length;
if (!pending)
return done(null, results);
list.forEach(function(file) {
file = path.resolve(dir, file);
fs.stat(file, function(err, stat) {
if (stat && stat.isDirectory()) {
walk(file, function(err, res) {
results = results.concat(res);
if (!--pending)
done(null, results);
});
} else {
results.push(file);
if (!--pending)
done(null, results);
}
});
});
});
};
I'm trying to follow it, and I think I understand everything except for near the end where it says !--pending. In this context, what does that command do?
Edit: I appreciate all the further comments, but the question has been answered many times. Thanks anyway!

! inverts a value, and gives you the opposite boolean:
!true == false
!false == true
!1 == false
!0 == true
--[value] subtracts one (1) from a number, and then returns that number to be worked with:
var a = 1, b = 2;
--a == 0
--b == 1
So, !--pending subtracts one from pending, and then returns the opposite of its truthy/falsy value (whether or not it's 0).
pending = 2; !--pending == false
pending = 1; !--pending == true
pending = 0; !--pending == false
And yes, follow the ProTip. This may be a common idiom in other programming languages, but for most declarative JavaScript programming this looks quite alien.

That's not a special operator, it's 2 standard operators one after the other:
A prefix decrement (--)
A logical not (!)
This causes pending to be decremented and then tested to see if it's zero.

A number of answers describes what this command does, but not why it is done that way here.
I come from the C world, and I read !--pending as "count down pending and check if it is zero" without really thinking about it. It is an idiom that I think programmers in similar languages should know.
The function uses readdir to get a list of files and subdirectories, which I will collectively call "entries".
The variable pending keeps track of how many of these remains to be processed. It starts out as the length of the list, and counts downward towards zero as each entry is processed.
These entries may be processed out of order, which is why it is necessary to count down rather than just using a simple loop. When all the entries have been processed the callback done is called to notify the original caller of this fact.
In the first call to done is prepended with return, not because we want to return a value, but simply to make the function stop executing at that point. It would have been cleaner code to drop the return and put the alternative in an else.

It's a shorthand.
! is "not".
-- decrements a value.
So !-- checks if the value obtained from negating the result of decrementing a value is false.
Try this:
var x = 2;
console.log(!--x);
console.log(!--x);
The first is false, since the value of x is 1, the second is true, since the value of x is 0.
Side note:
!x-- would check if x is false first, and then decrement it.

! is the JavaScript NOT operator
-- is a pre-decrement operator. So,
x = 1;
if (!x) // false
if (!--x) // becomes 0 and then uses the NOT operator,
// which makes the condition to be true

if(!--pending)
means
if(0 == --pending)
means
pending = pending - 1;
if(0 == pending)

It's the not operator followed by the in-place pre-decrementer.
So if pending was an integer with a value of 1:
val = 1;
--val; // val is 0 here
!val // evaluates to true

Explanation
This is 2 operators, a ! and a --
!--x
So, the -- decrements x by 1, then the ! returns true if x is now 0 (or NaN...), false if it isn't. You might read this idiom something like "we decrement x and if that makes it zero..."
If you wanted to make it more readable, you can:
var x = 1
x = x - 1
if(!x){ //=> true
console.log("I understand `!--` now!")
}
x //=> 0
Try it out:
/* This is an example of the above, you can read this, but it is not needed for !-- */function interactive(a){$("span.code").keydown(function(e){if(13==(e.keyCode||e.which)){var t=$(this);t.clone().html("code").insertAfter(t.next().next()).show().focus().after(template.clone().removeClass("result-template").show()).next().after("<br>"),interactive(),e.preventDefault()}}).keyup(function(e){13!=(e.keyCode||e.which)&&run()})}var template=$(".result-template").hide(),code=$("span.code");code.attr("contenteditable","true").each(function(e,t){template.clone().removeClass("result-template").insertAfter(t)}),interactive(),$.fn.reduce=[].reduce;function run(){var b=!1,context={};$("span.code").each(function(){var a=$(this),res=a.next().show().removeClass("error");try{with(context)res.html(b?"":" //=> "+eval(a.text()))}catch(e){b=e,res.html(" Error: "+b.message).addClass("error")}})};run();
/* This is an example of the above, you can read this, but it is not needed for !-- */span.result.error{display:block;color:red}.code{min-width:10px}body{font-family:Helvetica,sans-serif}
<!-- This is an example of the above, you can read this, but it is not needed for `!--` --><span class="result result-template"> //=> unknown </span> <h2>Edit This Code:</h2><code><span class="code">x = 1</span><br><span class="code">!--x</span><br><span class="code"> x </span><br></code> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Fiddle (Try Out Code)

It merely decreases pending by one and obtains its logical complement (negation). The logical complement of any number different than 0 is false, for 0 it is true.

The real problem here is the lack of a space between the two operators ! and --.
I don't know why people get it in their heads that you can't ever use a space after the ! operator. I think it comes from rigid application of mechanical whitespace rules instead of common sense. Just about every coding standard I've seen prohibits spaces after all unary operators, but why?
If there were ever a case where you clearly need that space, this is one.
Consider this bit of code:
if (!--pending)
done(null, results);
Not only are ! and -- mashed together, you've got that ( slammed against them too. No wonder it's hard to tell what is connected to what.
A bit more whitespace makes the code much more clear:
if( ! --pending )
done( null, results );
Sure, if you're used to mechanical rules like "no space inside parens" and "no space after a unary operator", this may seem a bit foreign.
But look at how the extra whitespace groups and separates the various parts of the if statement and expression: You've got --pending, so the -- is clearly its own operator and is tied closely to pending. (It decrements pending and returns the decremented result.) Then you've got the ! separated from that so it's obviously a distinct operator, negating the result. Finally, you've got if( and ) surrounding the whole expression to make it an if statement.
And yes, I removed the space between if and (, because the ( belongs to the if. This ( isn't part of some kind of (!-- syntax as it appears to be in the original, the ( if part of the syntax of the if statement itself.
The whitespace here serves to communicate the meaning, instead of following some mechanical coding standard.

Related

Why is my script saying '(' === ')' is true?

I was doing this kata on codewars. The question wants the function to return true if the first argument (string) passed in ends with the 2nd argument (also a string). So I wrote my function and everything worked just fine until it compares ':-)' with ':-(' and returns true.
What is wrong? I'm so confident that my code should work that I don't even know what to search for.
function solution(str, ending){
if (!ending) return true; // if ending is a empty string return true (the question wants that)
let ok;
const strArr = str.split(''), endingArr = ending.split('');
for (let i = 0; i < endingArr.length; i++) strArr.reverse()[i] === endingArr.reverse()[i] ? ok = true : ok = false;
return ok;
}
console.log(solution(":-)",":-("));
Your problem is a misunderstanding of what reverse() does. It does not return a reversed copy of the old array, it reverses the existing array and returns that same array. As a result, you keep reversing the arrays back and forth every iteration of the loop, causing some elements to be skipped and some to be checked twice.
Array.prototype.reverse() on MDN
Edit:
As pointed out by others in the comments, both to the question and this answer, there are in fact multiple problems.
reverse() aside, the loop always sets ok to the result of the last comparison, making the function ignore all previous results.
The easier way to implement this is to remove ok altogether. Instead, return false as soon as a mismatch is detected. If the function runs long enough to exit the loop, it means no mismatch was detected and true can be returned.
Edit 2:
Just as a friendly suggestion:
While both reverse() and ok are real issues with the code, I only noticed the first one the first time around due to the formatting of the code. The ok problem was off-screen due to the line being too long. As such, once I spotted the reverse() issue, I assumed that was it and didn't bother scrolling sideways to see the rest of the code.
I am not going to demand that you write your own code in a certain way, but if you format it properly, it allows others to read it more easily. In essence, you help us to more easily help you.
For instance, this line:
for (let i = 0; i < endingArr.length; i++) strArr.reverse()[i] === endingArr.reverse()[i] ? ok = true : ok = false;
...would have been significantly easier to read as...
for (let i = 0; i < endingArr.length; i++) {
if(strArr.reverse()[i] === endingArr.reverse()[i])
ok = true;
else
ok = false;
}
...or some variation thereof. Here, the problem is significantly more visible and obvious.
The other answer explains many of the mistakes you've made. I wanted to point out just how much you've over-thought your solution.
function solution(str, ending){
if (ending === "") return true; // if ending is a empty string return true (the question wants that)
return str.endsWith(ending);
}
console.log(solution(":-)",":-("));
console.log(solution("foo",""));
console.log(solution("foo","bar"));
console.log(solution("foobar","bar"));
Even my solution above is overengineered, str.endsWith("") always returns true. So this can be simplified further.
function solution(str, ending){
return str.endsWith(ending);
}
console.log(solution(":-)",":-("));
console.log(solution("foo",""));
console.log(solution("foo","bar"));
console.log(solution("foobar","bar"));

What is faster? Two comparisons or one comparison with one logical check?

Ok, while I was nesting some validation if, I've come into something about performance.
Let's suppose I'm validating a ticket to a show, I can do something like this:
var validateVipTicket = function (ticketInfo) {
if (ticketInfo.isPaid) {
if (ticketInfo.isOver16) {
if (ticketInfo.isVip) {
return true;
} else return 'Not Vip';
} else return 'Not old enought';
} else return 'Need to pay first';
};
And call it like this validateVipTicket({isPaid: true, isOver16: true, isVip: true});
BUT
let's suppose that does not matter to specify what error happened, I just want a true or false, then, what approach would be faster?
if (ticketInfo.isPaid) {
if (ticketInfo.isOver16) {
OR
if (ticketInfo.isPaid && ticketInfo.isOver16) {
On the first case, the first property will be checked and, when it finishes, the second property will be checked. On the second case, the first property will be checked, the second property will be checked and, them, both properties will be compared against each other. Is this right? Am I missing something? Which is faster and why?
On the second case, the first property will be checked, the second
property will be checked and, them, both properties will be compared
against each other. Is this right?
No, that's actually not right. The && operator does a short-circuit evaluation, which means that the second operand is never evaluated if the first operand evaluates to false.
That makes the two approaches exactly equivalent (even if there were any side effects of evaluating the oparands). As they do exactly the same amount of work in the same way, you can expect the performance to be very similar. It's even likely that the approaches end up generating the same executable code in the end.
It makes no difference:
if ( a && b )
is the same as
if ( a )
if ( b )
In the case of a && b, as soon as a is found false, then it does not matter what b is.
What happens at the CPU instruction level in both cases is:
cmp [a], 0 ; a == 0? (it does this by calculating a - 0)
jz false1 ; jump if result is zero
; a is true
cmp [b], 0
jz false2
; both a and b are true
Whether 3 different messages are returned, or just 1, could make a difference in speed, depending on how it is compiled. In this example, it makes no difference:
false1: return "not a!";
false2: return "not b!";
just_false: return false;
But here, an extra jump/goto is involved:
false1: result = "not a!"; goto done;
false2: result = "not b!"; // goto done optimized away
done: return result;
The 2 statements are equivalent in terms of performance. In both cases the second check (ticketInfo.isOver16) will be evaluated only if the first check (ticketInfo.isPaid) has returned true. Both statements are absolutely the same in terms of speed. So you should prefer the one that's more readable to you.
The performance in modern JS Engines such as V8 would be the same. Although there is always the chance that a crappy interpreter performs one better than the other but with modern browsers and JIT compilers such as V8 you should get the same results.
Proof of concept:
http://jsperf.com/nested-conditionals-vs-inline

JavaScript Short Circuit Logic

After seeing some examples online, I've collected two different explanations:
Ex: var x = A || B;
If A exists and B does not, left side is returned.
If A exists and B exists , return right side (last evaluated value).
Based on that logic, I would assume that x would return: v.item(0).click(). But when testing it x first returned B then A, aka fired B then fired A as well. Why? (http://jsfiddle.net/nysteve/QHumL/59/)
HTML:
<div class="indeed-apply-button" onclick="alert('BOOM BUTTON');">boo</div>
<div class='view_job_link' onclick="alert('BOOM LINK');">boo</div>
JavaScript
var v = document.getElementsByClassName('view_job_link');
var i = document.getElementsByClassName('indeed-apply-button');
var x = v.item(0).click() || i.item(0).click();
EDIT 1:02 PM 10/10/2013
Did not mention my true intentions, but based on the answer and discussion, my goal was essentially to convert the following piece of code into the JavaScript code I originally mentioned.
var v = document.getElementsByClassName('view_job_link');
var i = document.getElementsByClassName('indeed-apply-button');
if(v){v.item(0).click();}
else if(i){i.item(0).click();}
else{}
In the code above, how would you read if(v){v.item(0).click();} vs. the short-circuit?
Neither of your two descriptions are accurate.
var x = A || B;
What that does is:
Evaluate "A". Call the result VA.
If VA is not null, undefined, 0, NaN, false, or the empty string, then the value of the overall expression is VA and evaluation stops.
Evaluate "B". Call the result VB. That value, VB, is the value of the expression.
Your test code first tests the return value of calling the "click" function on the first element and then the second. Those functions both return undefined, so the subexpressions on both sides of the || are evaluated. That is, the first call gets VA and it's undefined, so that means that the other side will be evaluated too, and that'll be the value of the expression. (It's going to come out undefined.)
edit — OK now that you've added more to the answer, I think I see what you're up to.
In your actual code (or, I guess, the sample code that's closer to reality), you've got:
if(v){v.item(0).click();}
else if(i){i.item(0).click();}
else{}
That means something quite different than:
var x = v.item(0).click() || i.item(0).click();
Note that in the if statement version, it's explicitly checking "v". A test like that will perform a "truthy/falsy" test on the value of "v". In this case, it's really checking to make sure that "v" isn't either null or undefined. In the || version, however, there's no such explicit test for the "goodness" of variable "v"; it's just used directly. (If it happens to be null there, that'd result in a runtime error.)
A version of the actual code using || and && is possible, but in my opinion the existing code is clearer. However, just for discussion purposes, you could replace the if version with:
v && (v.item(0).click(), true) || i && i.item(0).click();
Personally I think that looks kind-of ugly.

How to shorten my conditional statements

I have a very long conditional statement like the following:
if(test.type == 'itema' || test.type == 'itemb' || test.type == 'itemc' || test.type == 'itemd'){
// do something.
}
I was wondering if I could refactor this expression/statement into a more concise form.
Any idea on how to achieve this?
Put your values into an array, and check if your item is in the array:
if ([1, 2, 3, 4].includes(test.type)) {
// Do something
}
If a browser you support doesn't have the Array#includes method, you can use this polyfill.
Short explanation of the ~ tilde shortcut:
Update: Since we now have the includes method, there's no point in using the ~ hack anymore. Just keeping this here for people that are interested in knowing how it works and/or have encountered it in other's code.
Instead of checking if the result of indexOf is >= 0, there is a nice little shortcut:
if ( ~[1, 2, 3, 4].indexOf(test.type) ) {
// Do something
}
Here is the fiddle: http://jsfiddle.net/HYJvK/
How does this work? If an item is found in the array, indexOf returns its index. If the item was not found, it'll return -1. Without getting into too much detail, the ~ is a bitwise NOT operator, which will return 0 only for -1.
I like using the ~ shortcut, since it's more succinct than doing a comparison on the return value. I wish JavaScript would have an in_array function that returns a Boolean directly (similar to PHP), but that's just wishful thinking (Update: it now does. It's called includes. See above). Note that jQuery's inArray, while sharing PHP's method signature, actually mimics the native indexOf functionality (which is useful in different cases, if the index is what you're truly after).
Important note: Using the tilde shortcut seems to be swathed in controversy, as some vehemently believe that the code is not clear enough and should be avoided at all costs (see the comments on this answer). If you share their sentiment, you should stick to the .indexOf(...) >= 0 solution.
A little longer explanation:
Integers in JavaScript are signed, which means that the left-most bit is reserved as the sign bit; a flag to indicate whether the number is positive or negative, with a 1 being negative.
Here are some sample positive numbers in 32-bit binary format:
1 : 00000000000000000000000000000001
2 : 00000000000000000000000000000010
3 : 00000000000000000000000000000011
15: 00000000000000000000000000001111
Now here are those same numbers, but negative:
-1 : 11111111111111111111111111111111
-2 : 11111111111111111111111111111110
-3 : 11111111111111111111111111111101
-15: 11111111111111111111111111110001
Why such weird combinations for the negative numbers? Simple. A negative number is simply the inverse of the positive number + 1; adding the negative number to the positive number should always yield 0.
To understand this, let's do some simple binary arithmetic.
Here is how we would add -1 to +1:
00000000000000000000000000000001 +1
+ 11111111111111111111111111111111 -1
-------------------------------------------
= 00000000000000000000000000000000 0
And here is how we would add -15 to +15:
00000000000000000000000000001111 +15
+ 11111111111111111111111111110001 -15
--------------------------------------------
= 00000000000000000000000000000000 0
How do we get those results? By doing regular addition, the way we were taught in school: you start at the right-most column, and you add up all the rows. If the sum is greater than the greatest single-digit number (which in decimal is 9, but in binary is 1) we carry the remainder over to the next column.
Now, as you'll notice, when adding a negative number to its positive number, the right-most column that is not all 0s will always have two 1s, which when added together will result in 2. The binary representation of two being 10, we carry the 1 to the next column, and put a 0 for the result in the first column. All other columns to the left have only one row with a 1, so the 1 carried over from the previous column will again add up to 2, which will then carry over... This process repeats itself till we get to the left-most column, where the 1 to be carried over has nowhere to go, so it overflows and gets lost, and we're left with 0s all across.
This system is called 2's Complement. You can read more about this here:
2's Complement Representation for Signed Integers.
Now that the crash course in 2's complement is over, you'll notice that -1 is the only number whose binary representation is 1's all across.
Using the ~ bitwise NOT operator, all the bits in a given number are inverted. The only way to get 0 back from inverting all the bits is if we started out with 1's all across.
So, all this was a long-winded way of saying that ~n will only return 0 if n is -1.
You can use switch statement with fall thru:
switch (test.type) {
case "itema":
case "itemb":
case "itemc":
case "itemd":
// do something
}
Using Science: you should do what idfah said and this for fastest speed while keep code short:
THIS IS FASTER THAN ~ Method
var x = test.type;
if (x == 'itema' ||
x == 'itemb' ||
x == 'itemc' ||
x == 'itemd') {
//do something
}
http://jsperf.com/if-statements-test-techsin
(Top set: Chrome, bottom set: Firefox)
Conclusion :
If possibilities are few and you know that certain ones are more likely to occur than you get maximum performance out if || ,switch fall through , and if(obj[keyval]).
If possibilities are many, and anyone of them could be the most occurring one, in other words, you can't know that which one is most likely to occur than you get most performance out of object lookup if(obj[keyval]) and regex if that fits.
http://jsperf.com/if-statements-test-techsin/12
i'll update if something new comes up.
If you are comparing to strings and there is a pattern, consider using regular expressions.
Otherwise, I suspect attempting to shorten it will just obfuscate your code. Consider simply wrapping the lines to make it pretty.
if (test.type == 'itema' ||
test.type == 'itemb' ||
test.type == 'itemc' ||
test.type == 'itemd') {
do something.
}
var possibilities = {
"itema": 1,
"itemb": 1,
"itemc": 1,
…};
if (test.type in possibilities) { … }
Using an object as an associative array is a pretty common thing, but since JavaScript doesn't have a native set you can use objects as cheap sets as well.
if( /^item[a-d]$/.test(test.type) ) { /* do something */ }
or if the items are not that uniform, then:
if( /^(itema|itemb|itemc|itemd)$/.test(test.type) ) { /* do something */ }
Excellent answers, but you could make the code far more readable by wrapping one of them in a function.
This is complex if statement, when you (or someone else) read the code in a years time, you will be scanning through to find the section to understand what is happening. A statement with this level of business logic will cause you to stumble for a few seconds at while you work out what you are testing. Where as code like this, will allow you to continue scanning.
if(CheckIfBusinessRuleIsTrue())
{
//Do Something
}
function CheckIfBusinessRuleIsTrue()
{
return (the best solution from previous posts here);
}
Name your function explicitly so it immediately obvious what you are testing and your code will be much easier to scan and understand.
You could put all the answers into a Javascript Set and then just call .contains() on the set.
You still have to declare all the contents, but the inline call will be shorter.
Something like:
var itemSet = new Set(["itema","itemb","itemc","itemd"]);
if( itemSet.contains( test.type ){}
One of my favorite ways of accomplishing this is with a library such as underscore.js...
var isItem = _.some(['itema','itemb','itemc','itemd'], function(item) {
return test.type === item;
});
if(isItem) {
// One of them was true
}
http://underscorejs.org/#some
another way or another awesome way i found is this...
if ('a' in oc(['a','b','c'])) { //dosomething }
function oc(a)
{
var o = {};
for(var i=0;i<a.length;i++) o[a[i]]='';
return o;
}
of course as you can see this takes things one step further and make them easy follow logic.
http://snook.ca/archives/javascript/testing_for_a_v
using operators such as ~ && || ((),()) ~~ is fine only if your code breaks later on. You won't know where to start. So readability is BIG.
if you must you could make it shorter.
('a' in oc(['a','b','c'])) && statement;
('a' in oc(['a','b','c'])) && (statements,statements);
('a' in oc(['a','b','c']))?statement:elseStatement;
('a' in oc(['a','b','c']))?(statements,statements):(elseStatements,elseStatements);
and if you want to do inverse
('a' in oc(['a','b','c'])) || statement;
Just use a switch statement instead of if statement:
switch (test.type) {
case "itema":case "itemb":case "itemc":case "itemd":
// do your process
case "other cases":...:
// do other processes
default:
// do processes when test.type does not meet your predictions.
}
Switch also works faster than comparing lots of conditionals within an if
For very long lists of strings, this idea would save a few characters (not saying I'd recommend it in real life, but it should work).
Choose a character that you know won't occur in your test.type, use it as a delimiter, stick them all into one long string and search that:
if ("/itema/itemb/itemc/itemd/".indexOf("/"+test.type+"/")>=0) {
// doSomething
}
If your strings happen to be further constrained, you could even omit the delimiters...
if ("itemaitembitemcitemd".indexOf(test.type)>=0) {
// doSomething
}
...but you'd have to be careful of false positives in that case (e.g. "embite" would match in that version)
For readability create a function for the test (yes, a one line function):
function isTypeDefined(test) {
return test.type == 'itema' ||
test.type == 'itemb' ||
test.type == 'itemc' ||
test.type == 'itemd';
}
then call it:
…
if (isTypeDefined(test)) {
…
}
...
I think there are 2 objectives when writing this kind of if condition.
brevity
readability
As such sometimes #1 might be the fastest, but I'll take #2 for easy maintenance later on. Depending on the scenario I will often opt for a variation of Walter's answer.
To start I have a globally available function as part of my existing library.
function isDefined(obj){
return (typeof(obj) != 'undefined');
}
and then when I actually want to run an if condition similar to yours I'd create an object with a list of the valid values:
var validOptions = {
"itema":1,
"itemb":1,
"itemc":1,
"itemd":1
};
if(isDefined(validOptions[test.type])){
//do something...
}
It isn't as quick as a switch/case statement and a bit more verbose than some of the other examples but I often get re-use of the object elsewhere in the code which can be quite handy.
Piggybacking on one of the jsperf samples made above I added this test and a variation to compare speeds. http://jsperf.com/if-statements-test-techsin/6 The most interesting thing I noted is that certain test combos in Firefox are much quicker than even Chrome.
This can be solved with a simple for loop:
test = {};
test.type = 'itema';
for(var i=['itema','itemb','itemc']; i[0]==test.type && [
(function() {
// do something
console.log('matched!');
})()
]; i.shift());
We use the first section of the for loop to initialize the arguments you wish to match, the second section to stop the for loop from running, and the third section to cause the loop to eventually exit.

Simple Confusing Loops and Variable working

In this question,I'm asking how the following snippets work, as it involves weird use of variable:
while (+(+i--)!=0)
{
i-=i++;
}
console.log(i);
Interesting problem... you've tagged it Java, JavaScript and C -- note that while these languages have the same syntax, this question involves very subtle semantics that may (I'm not sure) differ across languages.
Let's break it down:
+(+i--)
The -- postfix decrement operator is most tightly bound. So this is equivalent to +(+(i--)). That is therefore equivalent to the value of +(+i) (that is, i), but it also decrements i after taking the value. It compares the value with 0 to see if the loop should continue. Thus, while (+(+i--)!=0) is equivalent to the following:
while (i-- != 0)
Note that it also performs i-- at the end of the loop.
Inside the loop, I believe you have some undefined behaviour, at least in C, because you are referencing i on the right, and also updating i on the left -- I believe that C doesn't define which order to do that in. So your results will probably vary from compiler to compiler. Java, at least, is consistent, so I'll give the Java answer. i-=i++ is equivalent i = i - i++, which is equivalent to to reading all the values out of the expression, computing the result of the expression, applying the post-increment, and then assigning the result back. That is:
int t = i - i; // Calculate the result of the expression "i - i++"
i++; // Post-increment i
i = t; // Store the result back
Clearly, this is the same as writing i = 0. So the body of the loop sets i to 0.
Thus, the loop executes just one time, setting i to 0. Then, it decrements i one more time on the next while loop, but fails the check because i (before decrementing) == 0.
Hence, the final answer is -1, no matter what the initial value for i is.
To put this all together and write an equivalent program:
while (i-- != 0)
{
int t = i - i;
i++;
i = t;
}
console.log(i);
When I tried it in Java and JavaScript, that's what I got. For GCC (C compiler), it gives -1 only when i starts out as 0. If i starts out as anything else, it goes into an infinite loop.
That's because in GCC (not necessarily all C compilers), i-=i++ has a different meaning: it does the store back to i first, then does the post-increment. Therefore, it is equivalent to this:
int t = i - i; // Calculate the result of the expression "i - i++"
i = t; // Store the result back
i++; // Post-increment i
That's equivalent to writing i = 1. Therefore, on the first iteration, it sets i to 1, and then on the loop, it checks whether i == 0, and it isn't, so it goes around again, always setting i to 1. This will never terminate, but for the special case where i starts out as 0; then it will always terminate the loop and decrement i (so you get -1).
Another C compiler may choose to act like Java instead. This shows that you should never write code which both assigns and postincrements the same variable: you never know what will happen!
Edit: I tried to be too clever using that for loop; that wasn't equivalent. Turned back into a while loop.
That's soooo wierd! (I love it)
first, you can forget about the +(+...) part, it's just like saying 1 + (+1) = 2.
i-- means i = i - 1. In your while condition, you test if i-- is different from 0. Note: the test is made on i != 0 and then i's value is changed. If you wanted to change its value before the test, you should have used --i instead.
As for the i -= i++, it's a very dumb way to say i = 0. You must read it from right to left: i = i + 1 and then i = i - i1 (whatever value of i you have, you'll end up with 0.
Simplier quivalent snippet:
while (i-- != 0) {
i = 0;
}
console.log(i);
1 a -= b means a = a - b.
i -= i++ would mean a similar thing to i = i-i; i++ (if you make the -= explicit).
In a similar fashion, you can pull the side effect of i-- out of the loop condition by
transforming while (foo(i--)) { ... } to while (foo(i)) { i--; ...} i--; (you need to put it both in the loop body and after the loop because, at the end, the condition will be false and the loop body will not be executed but execution continues after the while loop).
The while condition evaluation happens based on operator precedence. I have used explicit braces to help understand the evaluation:
(+(+(i--)) != 0) which is equivalent to using (i-- != 0) as the '+' are just unary operators.
The expression i -=i++; is equivalent to i = i - i++; where the LHS expression gets evaluated from left to right.
i = 4;
while (+(+i--)!=0) //here i would be decremented once to 3.
{
i-=i++; // here i = 3 - 3 = 0; even though i has been incremented by 1 its value is set to 0 with this assignment
}
This is very simple. And I think the only reason to code like this is a concept called "code obfucasion" or "code confusing". This way makes the code harder to read and debug, so that can prevent from reverse engineer your code :-)

Categories

Resources