Jump to page: 1 2
Thread overview
Changing elements during foreach
Oct 21, 2013
Krzysztof Ciebiera
Oct 21, 2013
Alexandr Druzhinin
Oct 21, 2013
Krzysztof Ciebiera
Oct 21, 2013
Alexandr Druzhinin
Oct 21, 2013
Krzysztof Ciebiera
Oct 21, 2013
qznc
Oct 21, 2013
Jonathan M Davis
Oct 21, 2013
ixid
Oct 21, 2013
Jonathan M Davis
Oct 22, 2013
qznc
Oct 21, 2013
Dicebot
Oct 21, 2013
qznc
Oct 21, 2013
bearophile
October 21, 2013
Is the following compiler behavior consistent with language specification?

import std.stdio;

void main()
{
    int a[][] = [[1,2,3]];
    foreach(x; a)
    {
        x[0] = 0;
        x ~= 4;
    }
    writeln(a);
}

I understand why thw program could output [1,2,3] (like in C++ without &) or [0,2,3,4] (python, C++ ref). But [0,2,3]? It was unpleasant surprise.
October 21, 2013
21.10.2013 17:31, Krzysztof Ciebiera пишет:
> Is the following compiler behavior consistent with language specification?
>
> import std.stdio;
>
> void main()
> {
>      int a[][] = [[1,2,3]];
>      foreach(x; a)
>      {
>          x[0] = 0;
>          x ~= 4;
>      }
>      writeln(a);
> }
>
> I understand why thw program could output [1,2,3] (like in C++ without
> &) or [0,2,3,4] (python, C++ ref). But [0,2,3]? It was unpleasant surprise.
It's expected.
foreach(ref x; a) == C++ with &
but without ref you get the first element of a - i.e. [1, 2, 3] then set x[0] = 0 so you get [0, 2, 3]. But when you append 4 you append it to local copy of x - because there is no 'ref'. So your local copy of x became [0, 2, 3, 4] but when scope is gone you get unmodified copy of a with old array [0, 2, 3]. Changing the first element without ref possible because you modify inner array which is reference itself.
October 21, 2013
On Monday, 21 October 2013 at 10:41:38 UTC, Alexandr Druzhinin wrote:
> 21.10.2013 17:31, Krzysztof Ciebiera пишет:
>> void main()
>> {
>>     int a[][] = [[1,2,3]];
>>     foreach(x; a)
>>     {
>>         x[0] = 0;
>>         x ~= 4;
>>     }
>>     writeln(a);
>> }
>> ...
>> &) or [0,2,3,4] (python, C++ ref). But [0,2,3]? It was unpleasant surprise.
> It's expected.
> foreach(ref x; a) == C++ with &
> but without ref you get the first element of a - i.e. [1, 2, 3] then set x[0] = 0 so you get [0, 2, 3]. But when you append 4 you append it to local copy of x - because there is no 'ref'.

So, when exactly my local copy of data should be created? Now it is created during appending element to an array (when I switch instructions order, first append x ~= 4, then set x[0] to 0, as a result I get [1,2,3]). Maybe I should get a warning (like when hiding variable from outer scope)?
October 21, 2013
On Monday, 21 October 2013 at 10:31:51 UTC, Krzysztof Ciebiera wrote:
> Is the following compiler behavior consistent with language specification?
>
> import std.stdio;
>
> void main()
> {
>     int a[][] = [[1,2,3]];
>     foreach(x; a)
>     {
>         x[0] = 0;
>         x ~= 4;
>     }
>     writeln(a);
> }
>
> I understand why thw program could output [1,2,3] (like in C++ without &) or [0,2,3,4] (python, C++ ref). But [0,2,3]? It was unpleasant surprise.

The behavior of D's slices is often unintuitive. To understand them, read this article: http://dlang.org/d-array-article.html
October 21, 2013
Krzysztof Ciebiera:

> Is the following compiler behavior consistent with language specification?

Changing elements during foreach is something to avoid, perhaps I'd like it to be statically forbidden. If you add to this the reference-struct nature of arrays, you get in troubles.

Bye,
bearophile
October 21, 2013
21.10.2013 17:55, Krzysztof Ciebiera пишет:
> On Monday, 21 October 2013 at 10:41:38 UTC, Alexandr Druzhinin wrote:
>> 21.10.2013 17:31, Krzysztof Ciebiera пишет:
>>> void main()
>>> {
>>>     int a[][] = [[1,2,3]];
>>>     foreach(x; a)
>>>     {
>>>         x[0] = 0;
>>>         x ~= 4;
>>>     }
>>>     writeln(a);
>>> }
>>> ...
>>> &) or [0,2,3,4] (python, C++ ref). But [0,2,3]? It was unpleasant
>>> surprise.
>> It's expected.
>> foreach(ref x; a) == C++ with &
>> but without ref you get the first element of a - i.e. [1, 2, 3] then
>> set x[0] = 0 so you get [0, 2, 3]. But when you append 4 you append it
>> to local copy of x - because there is no 'ref'.
>
> So, when exactly my local copy of data should be created? Now it is
> created during appending element to an array (when I switch instructions
> order, first append x ~= 4, then set x[0] to 0, as a result I get
> [1,2,3]). Maybe I should get a warning (like when hiding variable from
> outer scope)?
If you switch instruction order you create local copy and then set x[0] in local copy so original is unchanged. But local copy creating depends on several thing and happens not every appending in general. Your way is not D-ish one. What do you want to do?
October 21, 2013
On Monday, 21 October 2013 at 14:59:54 UTC, Alexandr Druzhinin wrote:
> If you switch instruction order you create local copy and then set x[0] in local copy so original is unchanged. But local copy creating depends on several thing and happens not every appending in general. Your way is not D-ish one. What do you want to do?

I have an array of solutions to a problem. Each solution is an array of elements (not numbers, but objects of some classes). What I want to do is: I want to take a look at every single solution and extend it if it is possible (eg. if solution contains obbects a,b,c it can also contain d). The whole thing is rather big so I don't want to have any nice functional-style solution which will involve copying data. Especially since I have to perform similar extensions over and over again.

I understand slices now and I don't find it consistent with "no shoot in the foot by default" statement.
--
KC
October 21, 2013
On Monday, 21 October 2013 at 16:22:29 UTC, Krzysztof Ciebiera wrote:
> I understand slices now and I don't find it consistent with "no shoot in the foot by default" statement.

I agree. The pitfalls are well understood, yet everybody seems to love them. Ok, compared to C array they are an improvement due to bounds checking. If the elements are const or immutable (string) everything is fine, but write+append is basically implementation-defined behavior.

Once there is a proper std.collections, I will probably adapt my tutorial to recommend a safe alternative (ArrayList?).

October 21, 2013
On Monday, 21 October 2013 at 16:22:29 UTC, Krzysztof Ciebiera wrote:
> What I want to do is: I want to take a look at every single solution and extend it if it is possible (eg. if solution contains obbects a,b,c it can also contain d).

void main()
{
	import std.stdio;
	int[][] data = [ [ 0, 1, 2], [2, 3, 4] ];
	foreach(ref arr; data)
	{
		if (arr[0] == 2)
			arr ~= 5;
	}
	writeln(data);
}

?

(original arrays can be also allocated with extra capacity to avoid copy-allocation upon extending)
October 21, 2013
On Monday, October 21, 2013 21:16:00 qznc wrote:
> On Monday, 21 October 2013 at 16:22:29 UTC, Krzysztof Ciebiera
> 
> wrote:
> > I understand slices now and I don't find it consistent with "no shoot in the foot by default" statement.
> 
> I agree. The pitfalls are well understood, yet everybody seems to love them. Ok, compared to C array they are an improvement due to bounds checking. If the elements are const or immutable (string) everything is fine, but write+append is basically implementation-defined behavior.
> 
> Once there is a proper std.collections, I will probably adapt my tutorial to recommend a safe alternative (ArrayList?).

Just don't use slices when appending. It's only an issue when you're appending and relying on slices continuing to refer to the same array, and that's not going to work. Slices are primarily for reading, not writing. Using a container doesn't change that. It just makes it so that you can't even attempt to append to a slice, because the slice is a different type than that container and won't support appending.

- Jonathan m Davis
« First   ‹ Prev
1 2