Thread overview
Problem with AAs and ?:
Mar 08, 2008
bearophile
Mar 08, 2008
Frits van Bommel
Mar 08, 2008
bearophile
March 08, 2008
Can someone explain me where such difference comes from?
Is this a bug?

import std.stdio: writefln;

void main() {
    string[] words = ["how", "are", "you", "are"];

    int[string] aa1;
    foreach (w; words)
        aa1[w] = ((w in aa1) ? (aa1[w] + 1) : 2);
    writefln(aa1); // Prints: [how:1,you:1,are:2]

    int[string] aa2;
    foreach (w; words)
        if (w in aa2)
            aa2[w]++;
        else
            aa2[w] = 2;
    writefln(aa2); // Prints: [how:2,you:2,are:3]
}

Bye,
bearophile
March 08, 2008
"bearophile" <bearophileHUGS@lycos.com> wrote in message news:fqu9np$27ic$1@digitalmars.com...
> Can someone explain me where such difference comes from?
> Is this a bug?
>
> import std.stdio: writefln;
>
> void main() {
>    string[] words = ["how", "are", "you", "are"];
>
>    int[string] aa1;
>    foreach (w; words)
>        aa1[w] = ((w in aa1) ? (aa1[w] + 1) : 2);
>    writefln(aa1); // Prints: [how:1,you:1,are:2]
>
>    int[string] aa2;
>    foreach (w; words)
>        if (w in aa2)
>            aa2[w]++;
>        else
>            aa2[w] = 2;
>    writefln(aa2); // Prints: [how:2,you:2,are:3]
> }

This is odd.  Change the body of the first loop to:

auto val = (w in aa1) ? (aa1[w] + 1) : 2;
aa1[w] = val;

And they both output the same thing (2, 2, 3).

Sorry, breakfast time, I'll look at it a bit more afterwards ;)


March 08, 2008
bearophile wrote:
> Can someone explain me where such difference comes from?
> Is this a bug?
> 
> import std.stdio: writefln;
> 
> void main() {
>     string[] words = ["how", "are", "you", "are"];
> 
>     int[string] aa1;
>     foreach (w; words)
>         aa1[w] = ((w in aa1) ? (aa1[w] + 1) : 2);
>     writefln(aa1); // Prints: [how:1,you:1,are:2]
> 
>     int[string] aa2;
>     foreach (w; words)
>         if (w in aa2)
>             aa2[w]++;
>         else
>             aa2[w] = 2;
>     writefln(aa2); // Prints: [how:2,you:2,are:3]
> }

I looked at the generated code, and it seems that for AA insertions the compiler generates calls an internal routine (_aaGet) which returns a pointer to the location to put the value into (instead of inserting it directly). And that call is made *before* the new value is evaluated.
The result is that (w in aa1) evaluates to true because in order to be able to return that pointer the _aaGet routine allocates the relevant AA cell if it doesn't exist already; so by the time 'in' is evaluated there is indeed such a key present (its value just hasn't been set yet).

Essentially, the left side of the assignment is evaluated before the right side is.
I don't think this is technically a compiler bug with the current the specification; '=' doesn't determine order of evaluation AFAICT, so the compiler would seem to have every right to evaluate the left-hand-side first.

That's probably not what most users would expect, but I can see why this is done.
The combination of evaluating the right-hand-side of the assignment and writing it to the AA can in certain situations be more efficient if it's possible to write the value directly into the AA. (For example: "big" structs returned from functions are normally written to a caller-specified address so by evaluating the target location first "aa[key] = foo()" can pass the AA cell address to the function, which avoids an extra copy of the struct)
March 08, 2008
Frits van Bommel:
> I don't think this is technically a compiler bug with the current the specification; '=' doesn't determine order of evaluation AFAICT,

Even if technically it's not a compiler bug, it's seems a bug to me. I like D because it has less surprises than C++. To me a bit less (how much?) running speed is acceptable if similar bugs are avoided by my code.

Bye and thank you,
bearophile