www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - `clear`ing a dynamic array

reply Shriramana Sharma <samjnaa_dont_spam_me gmail.com> writes:
Hello. I had first expected that dynamic arrays (slices) would provide a 
`.clear()` method but they don't seem to. Obviously I can always effectively 
clear an array by assigning an empty array to it, but this has unwanted 
consequences that `[]` actually seems to allocate a new dynamic array and 
any other identifiers initially pointing to the same array will still show 
the old contents and thus it would no longer test true for `is` with this 
array. See the following code:

import std.stdio;
void main()
{
  int a[] = [1,2,3,4,5];
  int b[] = a;
  writeln(a);
  writeln(b);
  //a.clear();
  a = [];
  writeln(a);
  writeln(b);
}

which outputs:

[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[]
[1, 2, 3, 4, 5]

How to make it so that after clearing `a`, `b` will also point to the same 
empty array? IOW the desired output is:

[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[]
[]

... and any further items added to `a` should also reflect in `b`. 

-- 

Oct 24 2015
next sibling parent Cauterite <cauterite gmail.com> writes:
I'm afraid what you're asking for is impossible. Because 'a' and 
'b' are both slices, they each have their own 'length' field. 
When you do 'a = []', you're effectively doing 'a.length = 0'. 
There's no way to change 'b.length' through 'a'. To get that 
effect, you'd have to do something like this:
	int[] a = [1,2,3,4,5];
	int[]* b = &a;
	a = [];
	assert(*b == [] && b.length == 0);

On Saturday, 24 October 2015 at 13:18:26 UTC, Shriramana Sharma 
wrote:
 Hello. I had first expected that dynamic arrays (slices) would 
 provide a `.clear()` method but they don't seem to. Obviously I 
 can always effectively clear an array by assigning an empty 
 array to it, but this has unwanted consequences that `[]` 
 actually seems to allocate a new dynamic array and any other 
 identifiers initially pointing to the same array will still 
 show the old contents and thus it would no longer test true for 
 `is` with this array. See the following code:

 import std.stdio;
 void main()
 {
   int a[] = [1,2,3,4,5];
   int b[] = a;
   writeln(a);
   writeln(b);
   //a.clear();
   a = [];
   writeln(a);
   writeln(b);
 }

 which outputs:

 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5]
 []
 [1, 2, 3, 4, 5]

 How to make it so that after clearing `a`, `b` will also point 
 to the same empty array? IOW the desired output is:

 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5]
 []
 []

 ... and any further items added to `a` should also reflect in 
 `b`.
Oct 24 2015
prev sibling next sibling parent anonymous <anonymous example.com> writes:
On 24.10.2015 15:18, Shriramana Sharma wrote:
    int a[] = [1,2,3,4,5];
Aside: `int[] a;` is the preferred style for array declarations.
 How to make it so that after clearing `a`, `b` will also point to the same
 empty array? IOW the desired output is:

 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5]
 []
 []

 ... and any further items added to `a` should also reflect in `b`.
You can't do that with built-in arrays. The length of a dynamic array is a value member of the array structure. So to update `b`'s length you need access to the actual `b`.
Oct 24 2015
prev sibling next sibling parent John Colvin <john.loughran.colvin gmail.com> writes:
On Saturday, 24 October 2015 at 13:18:26 UTC, Shriramana Sharma 
wrote:
 Hello. I had first expected that dynamic arrays (slices) would 
 provide a `.clear()` method but they don't seem to. Obviously I 
 can always effectively clear an array by assigning an empty 
 array to it, but this has unwanted consequences that `[]` 
 actually seems to allocate a new dynamic array and any other 
 identifiers initially pointing to the same array will still 
 show the old contents and thus it would no longer test true for 
 `is` with this array. See the following code:

 import std.stdio;
 void main()
 {
   int a[] = [1,2,3,4,5];
   int b[] = a;
   writeln(a);
   writeln(b);
   //a.clear();
   a = [];
   writeln(a);
   writeln(b);
 }

 which outputs:

 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5]
 []
 [1, 2, 3, 4, 5]

 How to make it so that after clearing `a`, `b` will also point 
 to the same empty array? IOW the desired output is:

 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5]
 []
 []

 ... and any further items added to `a` should also reflect in 
 `b`.
D's arrays are not pure reference types, they work like `struct Array(T) { size_t length; T* ptr; }` with some extra methods and operators. If you think of them like that it should be clear what is/isn't possible. If you want to have two references to the same array, including the length, use T[]* or a ref argument to a function or wrap it in a class.
Oct 24 2015
prev sibling next sibling parent reply rsw0x <anonymous anonymous.com> writes:
On Saturday, 24 October 2015 at 13:18:26 UTC, Shriramana Sharma 
wrote:
 Hello. I had first expected that dynamic arrays (slices) would 
 provide a `.clear()` method but they don't seem to. Obviously I 
 can always effectively clear an array by assigning an empty 
 array to it, but this has unwanted consequences that `[]` 
 actually seems to allocate a new dynamic array and any other 
 identifiers initially pointing to the same array will still 
 show the old contents and thus it would no longer test true for 
 `is` with this array. See the following code:

 [...]
use std.container.array
Oct 24 2015
parent reply Shriramana Sharma <samjnaa_dont_spam_me gmail.com> writes:
rsw0x wrote:

 use std.container.array
Thanks all for all the recommendations. When would one use std.array.appender with a built-in array vs std.container.array.Array? What are the pros and cons on either side? --
Oct 24 2015
next sibling parent Olivier Pisano <olivier.pisano laposte.net> writes:
On Sunday, 25 October 2015 at 04:04:29 UTC, Shriramana Sharma 
wrote:
 rsw0x wrote:

 use std.container.array
Thanks all for all the recommendations. When would one use std.array.appender with a built-in array vs std.container.array.Array? What are the pros and cons on either side?
Appender is a small wrapper that enables you to get an output range from a built-in array. It allocates using the GC. If you have an array and you need to pass it to a function that takes an output range, you can use it. std.container.Array is a reference counted container that is equivalent to std::shared_ptr<std::vector<T>> in C++. It is not reliant on the GC.
Oct 25 2015
prev sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, October 25, 2015 09:34:25 Shriramana Sharma via Digitalmars-d-learn
wrote:
 rsw0x wrote:

 use std.container.array
Thanks all for all the recommendations. When would one use std.array.appender with a built-in array vs std.container.array.Array? What are the pros and cons on either side?
Appender is for when you you know that you're going to be doing a whole bunch of appending to an array, and you'd normally only use it when initially filling in the array, after which you'd just get the array out of it and use the array. Appender really isn't intended to be used as a container - just as a way to make appending more efficient or to have an output range which is an array but which is appended to by put rather than just assigning to its existing elements. You don't use Appender to not use arrays or to get full reference semantics from an array. If you want a container rather than a dynamic array - especially if you're looking for full reference semantics - then use std.container.array.Array. - Jonathan M Davis
Oct 25 2015
next sibling parent reply Shriramana Sharma <samjnaa_dont_spam_me gmail.com> writes:
Thanks all, for your replies.

Jonathan M Davis via Digitalmars-d-learn wrote:

 If you want a container rather than a dynamic array - especially if you're
 looking for full reference semantics - then use std.container.array.Array.
Hmmm, pardon me but while I'm sure I don't specifically require reference semantics, I'm not sure how you mean to contrast a "container" vs a "dynamic array". Both are aware of their content count and both are iterable, no? Is it that by "container" you mean that something that owns its contents and is responsible for deleting them all when it itself is deleted? --
Oct 25 2015
parent Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, October 25, 2015 16:23:14 Shriramana Sharma via Digitalmars-d-learn
wrote:
 Thanks all, for your replies.

 Jonathan M Davis via Digitalmars-d-learn wrote:

 If you want a container rather than a dynamic array - especially if you're
 looking for full reference semantics - then use std.container.array.Array.
Hmmm, pardon me but while I'm sure I don't specifically require reference semantics, I'm not sure how you mean to contrast a "container" vs a "dynamic array". Both are aware of their content count and both are iterable, no? Is it that by "container" you mean that something that owns its contents and is responsible for deleting them all when it itself is deleted?
Dynamic arrays are really pseudo-containers. They do not own or manage their own memory, and their memory and elements are potentially shared across multiple dynamic arrays. And mutating one dynamic array does not normally affect another one - even if they refer to the same memory - unless you're mutating its elements, and if one of them ends up having to be reallocated (e.g. because there wasn't enough room to append another element when an append operation was attempted), then two dynamic arrays which referred to the same memory would then refer to completely different memory. You started out this thread talking about how you wanted to be able to "clear" a dynamic array and have that affect other dynamic arrays which referred to the same memory, and that doesn't make any sense with a dynamic array, because dynamic arrays are not full reference types. They share the memory that they point to, but mutating the length of one (either directly or by adding or removing elements) does not affect any other dynamic array (except insofar as it can affect when an array would have to have its memory reallocated). That's why I talked about reference semantics. If you want a container that you pass around where removing an element from it or adding an element to it affects all of the other variables referring to that same data, you need an actual container type, not a dynamic array. If you haven't read this article yet http://dlang.org/d-array-article.html I'd suggest that you do. The terminology that it uses does not match the offial terminology (e.g. per the spec, T[] is a dynamic array regardless of what memory backs it, whereas that article refers to the GC-allocated buffer that backs most dynamic arrays as being the dynamic array), but it should make the semantics of D's dynamic arrays much clearer. - Jonathan M Davis
Oct 25 2015
prev sibling parent reply Shriramana Sharma <samjnaa_dont_spam_me gmail.com> writes:
Jonathan M Davis via Digitalmars-d-learn wrote:

 Appender really isn't intended to be used as a
 container - just as a way to make appending more efficient or to have an
 output range which is an array
I get the part about Appender helping to make an output range of a regular array, but I'm not sure how it is supposed to make appending "more efficient". I just had a look at the std.array code for appender and couldn't figure what was so special – obviously it's my limited knowledge. http://dlang.org/arrays.html#resize says: """Also, you may wish to utilize the phobos reserve function to pre-allocate array data to use with the append operator.""" I presume this means considered an operator is beyond me. Anyhow, is `reserve` the only thing that makes this more efficient? How is this more efficient than setting the .length of the dynamic array directly? I see there's a lot of logic going into ensureAddable() but doesn't this logic happen within druntime for the regular dynamic arrays itself? --
Oct 25 2015
next sibling parent reply anonymous <anonymous example.com> writes:
On Sunday, 25 October 2015 at 11:45:53 UTC, Shriramana Sharma 
wrote:
 http://dlang.org/arrays.html#resize says: """Also, you may wish 
 to utilize the phobos reserve function to pre-allocate array 
 data to use with the append operator."""

 I presume this means 

 how `append` is considered an operator is beyond me.
That sentence doesn't refer to std.array.Appender. `reserve` operator is `~=`.
Oct 25 2015
parent Shriramana Sharma <samjnaa_dont_spam_me gmail.com> writes:
anonymous wrote:

 I presume this means

 how `append` is considered an operator is beyond me.
That sentence doesn't refer to std.array.Appender. `reserve` operator is `~=`.
Thanks for that clarification. Now submitted a pull request. --
Oct 25 2015
prev sibling parent Jonathan M Davis via Digitalmars-d-learn writes:
On Sunday, October 25, 2015 17:15:50 Shriramana Sharma via Digitalmars-d-learn
wrote:
 Jonathan M Davis via Digitalmars-d-learn wrote:

 Appender really isn't intended to be used as a
 container - just as a way to make appending more efficient or to have an
 output range which is an array
I get the part about Appender helping to make an output range of a regular array, but I'm not sure how it is supposed to make appending "more efficient". I just had a look at the std.array code for appender and couldn't figure what was so special – obviously it's my limited knowledge.
There is bookkeeping in the GC involved with appending to a dynamic array (e.g. it has to look up whether there is room to grow into the buffer that backs the dynamic array or even whether the dynamic array is even backed by a GC-allocated buffer at all). Appender takes advantage of the fact that it's designed specifically for appending and keeps track of certain things on its own, bypassing a lot of what ~= normally does in an effort to make the specific use case of appending a bunch of times in a row efficient. So, it bypasses a lot of the checks that ~= is normally forced to do, but the result is that it's really just for constructing an array up front, whereas ~= can be used whenever, and I'm not sure that Appender even works correctly if you do something like get the array from it and start operating on it separately from the Appender and then continue to use the Appender. Really, you're supposed to use it to fill the array with its initial values, get the array out of the Appender, and then stop using the Appender. - Jonathan M Davis
Oct 25 2015
prev sibling parent qsdfghjk <qsdfghjk niwhere.hj> writes:
On Saturday, 24 October 2015 at 13:18:26 UTC, Shriramana Sharma 
wrote:
 Hello. I had first expected that dynamic arrays (slices) would 
 provide a `.clear()` method but they don't seem to. Obviously I 
 can always effectively clear an array by assigning an empty 
 array to it, but this has unwanted consequences that `[]` 
 actually seems to allocate a new dynamic array and any other 
 identifiers initially pointing to the same array will still 
 show the old contents and thus it would no longer test true for 
 `is` with this array. See the following code:

 import std.stdio;
 void main()
 {
   int a[] = [1,2,3,4,5];
   int b[] = a;
   writeln(a);
   writeln(b);
   //a.clear();
   a = [];
   writeln(a);
   writeln(b);
 }

 which outputs:

 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5]
 []
 [1, 2, 3, 4, 5]

 How to make it so that after clearing `a`, `b` will also point 
 to the same empty array? IOW the desired output is:

 [1, 2, 3, 4, 5]
 [1, 2, 3, 4, 5]
 []
 []

 ... and any further items added to `a` should also reflect in 
 `b`.
If you don't want to mess with pointers (as sugggested in the first answer) you can also use std.typecons.RefCounted: --- import std.stdio; import std.typecons; RefCounted!(int[]) b; void main() { int[] a = [1,2,3,4,5]; b = a; writeln(a); writeln(b); a = []; writeln(a); writeln(b); }
Oct 24 2015