"Double" assignment - should it be avoided?
Asked Answered
G

7

20

Consider you have some expression like

i = j = 0

supposing this is well-defined in your language of choice. Would it generally be better to split this up into two expressions like

i = 0
j = 0

I see this sometimes in library code. It doesn't seem buy you much in terms of brevity and shouldn't perform any better than the two statements (though that may be compiler dependant). So, is there a reason to use one over the other? Or is it just personal preference? I know this sounds like a silly question but it's bugging me for a long time now :-).

Gussie answered 13/11, 2010 at 11:14 Comment(0)
D
7

The two forms reflects different points of view on the assignment.

The first case treats assignment (at least the inner one) as an operator (a function returning a value).

The second case treats assignment as a statement (a command to do something).

There is some cases where the assignment as an operator has it's point, mostly for brevity, or to use in contexts that expect a result. However I feel it confusing. For several reasons:

  • Assignment operator are basically side effect operators, and nowadays it's a problem to optimize them for compilers. In languages like C and C++ they lead to many Undefined Behavior cases, or unoptimized code.
  • It is unclear what it should return. Should assignment operator return the value that as been assigned, or should it return the address of the place it has been stored. One or the other could be useful, depending on the context.
  • With composite assignments like +=, it's even worse. It is unclear if the operator should return the initial value, the combined result, or even the place it was stored to.

The assignment as a statement lead sometimes to intermediate variables, but that's the only drawback I see. It is clear and compilers know how to optimize efficiently successive such statements.

Basically, I would avoid assignment as operator whenever possible. The presented case is very simple and not really confusing, but as a general rule I would still prefer.

i = 0
j = 0

or

i, j = 0, 0

for languages that supports, parallel assignment.

Dyal answered 13/11, 2010 at 11:43 Comment(0)
V
16

Once upon a time there was a performance difference, which is one of the reason that this kind of assignment was used. The compilers would turn i = 0; j = 0; into:

load 0
store [i]
load 0
store [j]

So you could save an instruction by using i = j = 0 as the compiler would turn this into:

load 0
store [j]
store [i]

Nowadays compilers can do this type of optimisations by themselves. Also, as the current CPUs run several instructions at once, performance can no longer simply be measured in number of instructions. Instructions where one action doesn't rely on the result of another can run in parallel, so the version that uses a separate value for each variable might actually be faster.

Regarding programming style, you should use the way that best expresses the intention of the code.

You can for example chain the assignments when you simply want to clear some variables, and make it separate assignments when the value has a specific meaning. Especially if the meaning of setting one variable to the value is different from setting the other variable to the same value.

Vying answered 13/11, 2010 at 11:32 Comment(0)
D
7

The two forms reflects different points of view on the assignment.

The first case treats assignment (at least the inner one) as an operator (a function returning a value).

The second case treats assignment as a statement (a command to do something).

There is some cases where the assignment as an operator has it's point, mostly for brevity, or to use in contexts that expect a result. However I feel it confusing. For several reasons:

  • Assignment operator are basically side effect operators, and nowadays it's a problem to optimize them for compilers. In languages like C and C++ they lead to many Undefined Behavior cases, or unoptimized code.
  • It is unclear what it should return. Should assignment operator return the value that as been assigned, or should it return the address of the place it has been stored. One or the other could be useful, depending on the context.
  • With composite assignments like +=, it's even worse. It is unclear if the operator should return the initial value, the combined result, or even the place it was stored to.

The assignment as a statement lead sometimes to intermediate variables, but that's the only drawback I see. It is clear and compilers know how to optimize efficiently successive such statements.

Basically, I would avoid assignment as operator whenever possible. The presented case is very simple and not really confusing, but as a general rule I would still prefer.

i = 0
j = 0

or

i, j = 0, 0

for languages that supports, parallel assignment.

Dyal answered 13/11, 2010 at 11:43 Comment(0)
C
5

It depends on the language. In highly-object-oriented languages, double assignment results in the same object being assigned to multiple variables, so changes in one variable are reflected in the other.

$ python -c 'a = b = [] ; a.append(1) ; print b'
[1]
Castanets answered 13/11, 2010 at 11:16 Comment(2)
@kriss: But the question is not asking about that syntax.Castanets
I was pointing out that the equivalent of the one line combined assignement was not assignment of the same constant to both objets. This relies on convention that the result of assignment is the value that was assigned to the first variable. THis is one possible choice over many. In languages supporting implicit casts at assignment (like C++) it is not even obvious if the returned value should be the object before of after the cast is performed (I believe it's the first one, but would have to check in standard reference doc to be sure).Dyal
C
3

Firstly, at a semantic level, it depends whether you want to say that i and j are the same value, or just happen to both have the same value.

For example, if i and j are the indexes into a 2D array, they both start at zero. j = i = 0 says i starts at zero, and j starts where i started. If you wanted to start at the second row, you wouldn't necessarily want to start at the second column, so I wouldn't initialise them both in the same statement - the indices for rows and columns independently happen to both start at zero.

Also, in languages where i and j represent complicated objects rather than integral variables, or where assignment may cause an implicit conversion, they are not equivalent:

#include <iostream>

class ComplicatedObject
{
public:
    const ComplicatedObject& operator= ( const ComplicatedObject& other ) {
        std::cout << "    ComplicatedObject::operator= ( const ComplicatedObject& )\n";
        return *this;
    }
    const ComplicatedObject& operator= ( int value ) {
        std::cout << "    ComplicatedObject::operator= ( int )\n";
        return *this;
    }

};

int main ()
{
    {
        // the functions called are not the same
        ComplicatedObject i;
        ComplicatedObject j;

        std::cout << "j = i = 0:\n";
        j = i = 0;

        std::cout << "i = 0; j = 0:\n";
        i = 0;
        j = 0;
    }

    {
        // the result of the first assignment is 
        // effected by implicit conversion 
        double j;
        int i;

        std::cout << "j = i = 0.1:\n";
        j = i = 0.1;

        std::cout << "    i == " << i << '\n'
                  << "    j == " << j << '\n'
                  ;

        std::cout << "i = 0.1; j = 0.1:\n";
        i = 0.1;
        j = 0.1;

        std::cout << "    i == " << i << '\n'
                  << "    j == " << j << '\n'
                  ;
    }

}
Crowfoot answered 13/11, 2010 at 12:4 Comment(0)
S
2

Most of the people will find both possibilities equally readable. Some of these people will have a personal preference for either way. But there are people who might, at first glance, get confused by the "double assignment". I personally like the separate approach, bacause

  • It is 100% readable
  • It is not really verbose compared to the double variant
  • It allows me forget the rules of associativity for = operator
Sp answered 13/11, 2010 at 11:25 Comment(0)
J
1

The second way is more readable and clear, I prefer it.

However I try to avoid "double" declaration:

int i, j;

instead of

int i;
int j;

if they're going consecutively. Especially in case of MyVeryLong.AndComplexType

Jonna answered 13/11, 2010 at 11:15 Comment(2)
Not to mention char* i, j; versus char *i, *j;.Castanets
@Ignacio: Good old question related to your topic #377664Jonna
P
0

The reason you might want to use it is to ensure/show that:

  1. Both variables should be assigned the same value at the same time and have a dependency relationship (ie they are not decoupled from each other).
  2. You are making this relationship where one should match the other explicit.

This makes sense where one value directly depends on another's value or both depend on the same external context.

Here is an example of such a use case (in a Django project's settings.py file):

DEBUG = TEMPLATE_DEBUG = env("DEBUG")

Here it just makes sense that these related config variables are automatically assigned to the same value depending on whether the environment is development or production and that the dev team should be aware that both debugging and template debugging need to be enabled or disabled simultaneously.

However, you wouldn't only use it in a config variable but could conceivably use it in other parts of your code. (e.g. in a loop assigning two related variables) - conceivably anywhere where you would want to both assign them the same value and also have their dependency relationship be explicit.

Another example where you might really want to do this is where you are making an explicit shallow copy of an object and so it makes sense to be explicit in the initial assignment that these variables are related/dependent.

Pottle answered 22/11, 2023 at 16:20 Comment(1)
Btw TEMPLATE_DEBUG is a deprecated config variable in Django (see link) but the example still illustrates my point about assignment pretty clearly I think, so I'm just leaving that one in there. #32446453Pottle

© 2022 - 2024 — McMap. All rights reserved.