Using Assignment as a Condition Expression

assignment expressions with conditional expression

What about

z = int(y) if (y := expensive_function(x)).is_integer() else y

?


Actually, in a if cond else b, there are two conditional expressions: the a- and the b-members. But the middle member, i.e. the cond one is not conditional: it is always evaluated, explaining why using an assigment operator there raises no error.


A prior-to-3.8 approach (i.e. with no Walrus Operator) can be

z = (
lambda y: int(y) if y.is_integer() else y
)(
expensive_function(x)
)

Why would you use an assignment in a condition?

It's more useful for loops than if statements.

while(var = GetNext())
{
...do something with 'var'
}

Which would otherwise have to be written

var = GetNext();
while(var)
{
...do something
var = GetNext();
}

Assignment in conditional expression

= is assignment, but in conditional statements you need to check for equality (==), check if something is greater (>), check if something is less (<) etc. You are assigning the variable j the length of myName rather than checking some condition on this line:

for(var j = i;j = myName.length;){

Instead you probably need to do something like this:

for(var j = i;j <= myName.length;){

However, this may not necessarily be the solution to your Codecademy Assignment, but it will solve your specific javascript error. play around with and read up on <, >, == and the other conditionals mentioned here to try to figure out what works.

Edit: if you wanted a solution to your entire problem, it would have been helpful to post a link to the problem in the question and not only mention the specific error you were getting, but explain the entire question. That being said, you missed a few things here:

  1. You were doing assignment instead of checking a condition as I explained above.
  2. You forgot to increment j in your for loop as Franklin mentioned in the comments. You need to do j++.
  3. You are not stopping at the correct point in the string. As Codecademy says, "...your second for loop should stop when it reaches its current point in the string + myName.length." This means that you need to stop at text.length + myName.length instead of just myName.length. That also means you should use < rather than <= as I recommended above.

Putting all of that together, the solution is to put this line:

for(var j = i;j < (text.length + myName.length); j++){

in place of of this line:

for(var j = i;j = myName.length;){

javascript: using an assignment statement where a conditional expression would normally be

if (geo = getGeoLocation()) {  
// ...
}

My question is, from the perspective of the if statement, what is being returned when
geo = getGeoLocation() is evaluated?

Here are the chronological steps of what will happen:

  • getGeoLocation() will execute first
  • assignment operation will happen next, where whatever getGeolocation() returns will be stored in geo

    • This will be either undefined or navigator.geolocation, if it exists.
  • geo will then be evaluated (tested for truthiness) as the
    condition to the if statement

The equivalent of that code is the following:

geo = getGeoLocation();
if (geo) {
// ...
}

Expressions with conditional and assignment operator

Here are the two keys to understanding the difference between the JavaScript conditional expression and the Java conditional expression:

Please read the Note at the bottom of this section of the ECMAScript 5 annotated specification:

http://es5.github.io/#x11.12

Now, please read the Java specification section for the conditional expression:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.25

You will note that as the ECMAScript 5 note states, the third operand in the ternary operator in Java cannot be just any old expression - it can only be a ConditionalExpression. However, for ECMAScript 5, the third operand can be any AssignmentExpression.

Looking further at the Java spec, we see that Expression is any assignment expression:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.27

But ConditionalExpression is either the ConditionalExpression with the ternary operator (... ? ... : ...) or just a ConditionalOrExpression (termed LogicalOrExpression in ES5) (see either of the first two links above for that info). The "chain" of what the ConditionalOrExpression can be starts here in Java:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.24

And here in ECMAScript 5:

http://es5.github.io/#x11.11

Following the "chain" of expression types backward in the ECMAScript 5 spec (because it is easier to follow than the Java spec) from ConditionalExpression all the way through basically every other expression but Assignment Expression finally lands us at the beginning - Primary Expression:

http://es5.github.io/#x11.1

The second operand in both of your code snippets above is a primary expression:

1

The upshot of all this rigamarole (if I am correct) is that in Java, the third operand of the ternary operator cannot be an assignment, but in JavaScript it can. That is why both of your examples fail in Java but only the second in JavaScript.

Why does the first one work in JavaScript but not the second?

operand1 ? operand2 : operand3;

works like the following IIFE code (not actually, but the below code is illustrative of how the above works):

(function () { if (operand0) return operand1; else return operand2;}());

So:

false ? 1 : x = 2;

becomes (again, not actually - the below code is illustrative of the above code):

(function () { if (false) return 1; else return x = 2;}());

However, in your second snippet, when using the parens, you separate out explicitly the conditional expression from the ' = 2;':

(false ? 1 : x) = 2;

becomes (again, not actually - the below code is illustrative of the above code):

(function () { if (false) return 1; else return x;}()) = 2;

The "acts like the example IIFE function invocation" behavior of the ternary operator will return whatever x is and that will be a value, not a reference, which cannot be assigned to. Hence the error. This would be like the following code (if x === 3):

3 = 2;

Obviously, one cannot do this.

In Java, I believe, the first one gives an error because the third operator cannot be an assignment, the second one gives an error because you cannot assign to a value (just like in JavaScript).

As far as operator precedence, please look at the following code:

var x = 3;

console.log(false ? 1 : x); // ?: evaluates to "3"
console.log(false ? 1 : x = 2); // ?: evaluates to "2"
console.log(false ? 1 : x = 2, 4); // ?: evaluates to "2" - "2" and "4" arguments passed to log
console.log((false ? 1 : x = 2, 4)); // ?: evaluates to "4"

The first two are easily understood when viewed in terms of the IIFE illustrative code above.

In the first line x is evaluated and the conditional expression evaluates to 3 - that's easy.

In the second line, the best way I can describe it is that the conditional operator (?:) causes even the lower precedence '=' operator to be evaluated as a complete expression not because (?:) has higher precedence, but because as the spec states the assignment expression following the ':' is evaluated (including the ' = 2' part) as an AssignmentExpression. This behavior looks clearer in the return statement in the IIFE examples above. With JavaScript at least, you can have an assignment not only in the second operand but also the third of the conditional expression.

However, in the third line, a complete assignment expression is already found in the "x = 2" expression and the ternary operator uses it as the complete third operand, and the ',' operator being lower in precedence than any other, we get the equivalent to the following code:

console.log((false ? 1 : x = 2), 4);

In the fourth line of code, encapsulating the entire expression within the console.log() statement in parens brings the ', 4' into the '?:' ternary expression as part of the third operand.

The following jsfiddles demonstrate the above discussion with live code. Note that the first two have the same exact error after printing '2' twice:

FIDDLE1

FIDDLE2

FIDDLE3

How the assignment statement in an if statement serves as a condition?

Quoting C11, chapter §6.5.16, Assignment operators (emphasis mine)

An assignment operator stores a value in the object designated by the left operand. An
assignment expression has the value of the left operand after the assignment,111) but is not
an lvalue.

So, first the assignment will happen, and then, the value that has been assigned will be used as the conditional statement in if.

So, in case of

if (p = 0 )

will evaluate to FALSE and

if (p = 5)

will be TRUE.

Can we have assignment in a condition?

Why not try it out?

>>> def some_func():
... return 2
...
>>> if (a = some_func()):
File "<stdin>", line 1
if (a = some_func()):
^
SyntaxError: invalid syntax

So, no.

Update: This is possible (with different syntax) in Python 3.8

if a := some_func():

Assignment expression in while condition is a bad practice?

There should be no performance problems with it (arguably, indexing with prefix increment can be slightly slower than postfix increment, due to issues with CPU pipelines; this is a microoptimization so ridiculously micro that it almost certainly means nothing in the context of JS engine overhead, even in C the compiler is likely to reorder expressions if it can to ensure it's not stalled waiting on the increment).

Either way, the main argument against assignment in a conditional is basically that most of the time when you do it, it's a mistake (you meant == or in JS, ===). Some code checkers (and C# requires this as a language feature to avoid accidents) are satisfied if you wrap the assignment in an additional layer of parens, to say, "Yup, I really meant to assign" (which is also necessary when you're comparing the result of the assignment to some other value; omitting the parens would instead compare, then assign a boolean, which even more likely to be wrong).

Some people have a hate on for increment/decrement operators used as part of larger expressions, because remembering order of operations is hard I guess, and because C programmers have been known to write horrible things like ++*++var and the like. I ignore these people; just don't use it for excessively tricky things.

What is wrong with this assignment in a conditional operator?

a[i]>90 ? a[i]=a[i]-32 : a[i]=a[i]+32;

is not evaluated as

a[i]>90 ? (a[i]=a[i]-32) : (a[i]=a[i]+32);

since = has lower precedence than ?:. In standard C you can't write it as above although some compilers allow it as an extension.

You could write it as the more readable (and portable)

a[i] += a[i] > 90 ? -32 : +32;


Related Topics



Leave a reply



Submit