digitalmars.D.learn - Changing behavior of associative array
- Kevin Bailey (45/45) Dec 16 2023 Perhaps someone can help solve this mystery. I have a sample
- kdevel (9/14) Dec 16 2023 If you comment out this line
- Dennis (11/18) Dec 16 2023 That's because `m[f] = 1` initializes the associative array to
- Julian Fondren (22/25) Dec 16 2023 I've gotten this error in deployed Perl. Whenever the ceremony of
- Kevin Bailey (6/16) Dec 16 2023 Thanks Dennis. I'll have to tweak my mental model of what's
- Julian Fondren (3/6) Dec 16 2023 Yes:
Perhaps someone can help solve this mystery. I have a sample program that adds structs to an associative array (as keys) in a subroutine. The program passes the array by reference as expected (below). My real program does the same thing and yet the associative array is passed by value. Thus anything added in the subroutine is lost. I've added a TON of writeln's to confirm this. The *instant* I made the parameter "ref", the real program starts acting as expected, and the print-outs show the correct results. Does anyone know why the behavior would change? Has anyone seen anything like this? I even tried importing the same libraries into both programs. The only remaining difference is that the real function has several more arguments (7), but I would be shocked if D changed its behavior because of that. thx ``` import std.stdio; struct Foo { uint a, b, c; } void add_one_and_recurse( uint[Foo] m, int x) { if (!x) return; foreach (k,v; m) // Prints the 1 from main() writeln(k, ", ", v); // as expected. add_one_and_recurse(m, x-1); Foo f = {4, 5, x}; m[f] = 7; } int main() { uint[Foo] m; Foo f = {1, 2, 3}; m[f] = 1; add_one_and_recurse(m, 3); foreach (k,v; m) // Prints 4 items writeln(k, ", ", v); // as expected. return 0; } ```
Dec 16 2023
On Saturday, 16 December 2023 at 20:04:54 UTC, Kevin Bailey wrote:I've added a TON of writeln's to confirm this. The *instant* I made the parameter "ref", the real program starts acting as expected, and the print-outs show the correct results.If you comment out this line ``` // m[f] = 1; ``` in your main function of your posted code you can catch up with your real programm insofar as you now need a ref parameter here, too.Does anyone know why the behavior would change? Has anyone seen anything like this?Sure.
Dec 16 2023
On Saturday, 16 December 2023 at 21:30:55 UTC, kdevel wrote:If you comment out this line ``` // m[f] = 1; ``` in your main function of your posted code you can catch up with your real programm insofar as you now need a ref parameter here, too.That's because `m[f] = 1` initializes the associative array to something non-null. If you pass a `null` AA to a function which adds things, the caller will still have a null pointers. You can initialize a non-null empty AA like this: ```D uint[Foo] m = new uint[Foo]; ``` Then, `m` can be passed by value and you can make additions or removals which the caller sees, unless you assign a new AA in the function.
Dec 16 2023
On Saturday, 16 December 2023 at 22:44:16 UTC, Dennis wrote:That's because `m[f] = 1` initializes the associative array to something non-null. If you pass a `null` AA to a function which adds things, the caller will still have a null pointers.I've gotten this error in deployed Perl. Whenever the ceremony of creating a data structure is reduced, people can lose sight of when that happens. Here's a less common gotcha: ```d void main() { import std.stdio : writeln; int force_realloc(ref int[] seq) { foreach (int i; 1 .. 1_000_000) { seq ~= i; } return 1234; } int[] a = [4321]; writeln(a[0]); // 4321, of course a[0] = force_realloc(a); writeln(a[0]); // still 4321 } ``` The `a[0]` on the left of the assignment is decided early, and then force_realloc() changes what the location should be.
Dec 16 2023
On Saturday, 16 December 2023 at 22:44:16 UTC, Dennis wrote:That's because `m[f] = 1` initializes the associative array to something non-null. If you pass a `null` AA to a function which adds things, the caller will still have a null pointers. You can initialize a non-null empty AA like this: ```D uint[Foo] m = new uint[Foo]; ``` Then, `m` can be passed by value and you can make additions or removals which the caller sees, unless you assign a new AA in the function.Thanks Dennis. I'll have to tweak my mental model of what's happening. I was picturing a std::map-like thing that was passed by reference, but instead it seems like 'm' is a pointer to a std::map, that is initialized on use if null. (I think it's that latter part that gives the illusion of being already initialized.)
Dec 16 2023
On Sunday, 17 December 2023 at 00:10:56 UTC, Kevin Bailey wrote:instead it seems like 'm' is a pointer to a std::map, that is initialized on use if null. (I think it's that latter part that gives the illusion of being already initialized.)Yes: https://dlang.org/spec/hash-map.html#construction_and_ref_semantic
Dec 16 2023