www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Empty non-null Associative Arrays should be trivial or even the

reply Rekel <paultjeadriaanse gmail.com> writes:
Having to insert dummy variables into an associative array in 
order to initialize it (and prevent foreach loops from throwing a 
range violation error) seems very dirty to me. Why is there no 
syntax for empty initialization?

Suddenly getting a nullpointer after removing a ~= in an entirely 
different part of my code is 1 (VERY problematic) thing, but I'm 
even more surprised by the possible 'fixes'. . . (which would be 
either a useless insertion & removal procedure or a nullpointer 
check I will never again need after initial steps.)

Is there any plan of resolving this issue? I'm shocked this is 
even a thing at all.
Aug 03 2021
next sibling parent reply Mike Parker <aldacron gmail.com> writes:
On Tuesday, 3 August 2021 at 15:51:43 UTC, Rekel wrote:
 Having to insert dummy variables into an associative array in 
 order to initialize it (and prevent foreach loops from throwing 
 a range violation error) seems very dirty to me. Why is there 
 no syntax for empty initialization?
Associative arrays are initialized as empty by default. The following compiles and prints nothing, as expected: ```d int[string] aa; foreach(k, v; aa) writeln(v); ``` What are you doing that causes the range violation?
 Suddenly getting a nullpointer after removing a ~= in an 
 entirely different part of my code is 1 (VERY problematic) 
 thing, but I'm even more surprised by the possible 'fixes'. . . 
 (which would be either a useless insertion & removal procedure 
 or a nullpointer check I will never again need after initial 
 steps.)
You mean, doing something like `aa["foo"]` gives you the range violation? That's intended. If the key was never assigned, it's just like trying to index a normal array with a value that's out of bounds. The proper way to check for an element in an AA is via `in`, which returns a pointer to the element if it exists and null if it doesn't: ```d int[string] aa; if(auto v = "foo" in aa) writeln(*v); else writeln("No foo."); ``` See: https://dlang.org/spec/hash-map.html#testing_membership
Aug 03 2021
parent Rekel <paultjeadriaanse gmail.com> writes:
On Tuesday, 3 August 2021 at 16:20:50 UTC, Mike Parker wrote:
 You mean, doing something like `aa["foo"]` gives you the range 
 violation? That's intended. If the key was never assigned, it's 
 just like trying to index a normal array with a value that's 
 out of bounds. The proper way to check for an element in an AA 
 is via `in`, which returns a pointer to the element if it 
 exists and null if it doesn't:
Yes, you are absolutely correct. You've been faster at responding than I have been at backtracking my previous statements, that is very impressive 😅, thanks for helping out.
Aug 03 2021
prev sibling parent reply Rekel <paultjeadriaanse gmail.com> writes:
Feel free to disregard my previous post seems valid, as I seem to 
have misunderstood the meaning of AA's being null. (Wish I'd 
triple checked, it was an incorrect use of ) Sorry for wasting 
anyone's time...

How can all properties still be callable with a null-AA? This 
surprises me.

Also a small sidequestion; how come remove is part of AA's 
definition while the removal of items from dynamic lists is part 
of the library instead?

---
https://tenor.com/view/justin-timberlake-jt-bad-teacher-stupid-ifeel-stupid-gif-3547095

For those interested, my range error was caused by the following 
mistake;
```d
int[][int] aa;

void addElement(int element) {
	aa[0] ~= element;
}

void main(string[] args) { // Works
	addElement(0);
	aa[0].writeln();
}

void main(string[] args) { // Does not work
	aa[0].writeln();
}
```

Assumptions thus on present elements, although the first main 
does not allude to its absence. In particular, why can one `~=` 
when aa[0] does not exist?
Likewise, why is this valid:?

```d
int[] a;
assert(a is null); // Strange to me this works as many have said 
dynamic arrays are structs.
a ~= 1;

writeln(a);
writeln([] ~ 1);
// But not writeln(null ~ 1);
```

---

Again sorry for my previous mistake. I'll be more careful in the 
future.
- Paul
Aug 03 2021
next sibling parent Mike Parker <aldacron gmail.com> writes:
On Tuesday, 3 August 2021 at 16:28:03 UTC, Rekel wrote:

 Also a small sidequestion; how come remove is part of AA's 
 definition while the removal of items from dynamic lists is 
 part of the library instead?
AAs have to do internal bookkeeping, with the buckets and such, that dynamic arrays do not have to worry about. So they've had their own remove function from the beginning. As I recall, dynamic arrays didn't have a remove function back in the D1 days. Removal was a matter of iteration and slicing: ```d foreach(i, e; arr) { if(e == toRemove) { arr = a[0 .. i] ~ a[i+1 .. $]; break; } } ``` And the remove function we have now is not specifically for dynamic arrays. It's for ranges. Dynamic arrays happen to be ranges (courtesy of the range API implemented for them in std.array) so you can use them with that function and any function in std.algorithm. Static arrays and associative arrays are not ranges.
Aug 03 2021
prev sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 8/3/21 12:28 PM, Rekel wrote:
 Feel free to disregard my previous post seems valid, as I seem to have 
 misunderstood the meaning of AA's being null. (Wish I'd triple checked, 
 it was an incorrect use of ) Sorry for wasting anyone's time...
 
 How can all properties still be callable with a null-AA? This surprises me.
An AA is really a struct with a pointer to an implementation struct. Therefore, a null AA is still valid, it just has a null implementation (which is treated as an empty one). For example, AA length property looks like: ```d size_t aaLength(AA *impl) { if(impl is null) return 0; return impl.length; } ``` or something along those lines.
 
 Also a small sidequestion; how come remove is part of AA's definition 
 while the removal of items from dynamic lists is part of the library 
 instead?
A remove of an AA is an amortized O(1) operation, which makes it fast enough to be a builtin property. Removal from a dynamic array involves shifting elements around, and is O(n), so it's left up to a library to do if it wishes.
 
 ---
 https://tenor.com/view/justin-timberlake-jt-bad-teacher-stupid-ifee
-stupid-gif-3547095 
 
 
 For those interested, my range error was caused by the following mistake;
 ```d
 int[][int] aa;
 
 void addElement(int element) {
      aa[0] ~= element;
 }
 
 void main(string[] args) { // Works
      addElement(0);
      aa[0].writeln();
 }
 
 void main(string[] args) { // Does not work
      aa[0].writeln();
 }
 ```
AA's have a special behavior depending on whether your indexed value is used as an rvalue or lvalue. So for instance `writeln(aa[0])` is using `aa[0]` as an rvalue, so no dummy element is inserted. However, `aa[0] ~= element` requires an lvalue, so one is added if it doesn't exist. -Steve
Aug 03 2021