assignment: left-to-right or right-to-left evaluation?
Steven Schveighoffer
schveiguy at yahoo.com
Mon May 11 04:34:38 PDT 2009
On Sat, 09 May 2009 19:15:59 -0400, Derek Parnell <derek at psych.ward> wrote:
> On Sat, 09 May 2009 11:43:09 -0500, Andrei Alexandrescu wrote:
>
>> Consider:
>>
>> uint fun();
>> int gun();
>> ...
>> int[] a = new int[5];
>> a[fun] = gun;
>>
>> Which should be evaluated first, fun() or gun()? It's a rather arbitrary
>> decision. C/C++ don't even define an order. Python chooses
>> left-to-right, EXCEPT for assignment, which is right-hand side first.
>> Lisp and C# choose consistent left-to-right. I don't like exceptions and
>> I'd like everything to be left-to-right. However, this leads to some odd
>> cases. Consider this example in TDPL:
>>
>> import std.stdio, std.string;
>>
>> void main() {
>> uint[string] dic;
>> foreach (line; stdin.byLine) {
>> string[] words = split(strip(line));
>> foreach (word; words) {
>> if (word in dic) continue; // nothing to do
>> uint newID = dic.length;
>> dic[word] = newID;
>> writeln(newID, '\t', word);
>> }
>> }
>> }
>>
>> If we want to get rid of newID, we'd write:
>>
>> writeln(dic.length, '\t', word);
>> dic[word] = dic.length;
>>
>> by the Python rule, and
>>
>> writeln(dic.length, '\t', word);
>> dic[word] = dic.length - 1;
>>
>> by the C# rule.
>>
>> What's best?
>
> I'm sure about 'best', but I'd prefer the Python method.
Think you meant 'not sure' :)
>
> The example is similar to ...
>
> array = array ~ array.length;
>
> in as much as the result of the assignment is that the array length
> changes, but here it more easy to see that the pre-assignment length is
> being used by the RHS.
>
> In COBOL-like syntax ...
>
> move dic.length to dic[word].
>
> it is also more obvious what the coder's intentions were.
>
> In assembler-like syntax (which is what eventually gets run, of course)
> ...
>
> mov regA, dic.length
> mov dic[word], regA
>
> It just seems counter-intuitive that the target expression's side-effects
> should influence the source expression.
>
This reasoning makes the most sense, but let's leave COBOL out of it :)
I vote for the Python method too. It's how my brain sees the expression.
Also consider like this:
uint len;
mydic[x] = len = mydic.length;
Now, it's even more obvious that len = mydic.length should be immune to
the effects of mydic[x]. Longer chained assignment expressions seem like
they would make the problem even harder to understand if it's all
evaluated left to right. You may even make code more bloated because of
it.
For example:
mydic[x] = mydic[y] = mydic[z] = mydic.length;
if evaluating right to left, this looks like:
1. calculate mydic.length, store it in register A.
2. lookup mydic[z], if it doesn't exist, add it. Store register A to it.
3. lookup mydic[y], if it doesn't exist, add it. Store register A to it.
4. ditto for mydic[x]
If evaluating left to right, this looks like:
1. lookup mydic[x], if it doesn't exist, add it. Store a reference to it
on the stack.
2. lookup mydic[y], if it doesn't exist, add it. Store a reference to it
on the stack.
3. lookup mydic[z], if it doesn't eixst, add it. Store the reference to
it in register B.
4. calculate mydic.length, store it in register A. Store the result in
the reference pointed to by register B.
5. pop register B from the stack, store register A to the value it
references.
6. Repeat step 5.
Two extra steps, and I have to use a stack. Maybe 3 chained assignments
would be easy to store without a stack, but try 10 chained assignments.
I'd think the compiler code to evaluate right to left would be simpler
also, because you can reduce the expression at every assignment.
-Steve
More information about the Digitalmars-d
mailing list