www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Associative Array c'tor

reply Bahman Movaqar <Bahma BahmanM.com> writes:
I'm processing a list of structs (MySt) with `reduce` to produce an
associate array of type `MySt[][string]`.
Right now I'm using the following (slimmed down) code:

    MySt[][string] result;
    return reduce!(
      function MySt[][string](MySt[][string] acc, MySt val) {
        // do something with acc
        return acc;
      }
    )(result, inputList);

I was wondering if I could remove the empty declaration line (line 1);
for example:

    return reduce!(
      function MySt[][string](MySt[][string] acc, MySt val) {
        // do something with acc
        return acc;
      }
    )(new MySt[][string](), inputList);

Obviously, this fails with the error message: "cannot pass type string
as a function argument".

I very much would like to avoid empty declaration lines like that of
`result`.  Is there anyway you folks would suggest?

Thanks in advance,
--
Bahman
Jul 11 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/11/16 9:48 AM, Bahman Movaqar wrote:
 I'm processing a list of structs (MySt) with `reduce` to produce an
 associate array of type `MySt[][string]`.
 Right now I'm using the following (slimmed down) code:

     MySt[][string] result;
     return reduce!(
       function MySt[][string](MySt[][string] acc, MySt val) {
         // do something with acc
         return acc;
       }
     )(result, inputList);

 I was wondering if I could remove the empty declaration line (line 1);
 for example:

     return reduce!(
       function MySt[][string](MySt[][string] acc, MySt val) {
         // do something with acc
         return acc;
       }
     )(new MySt[][string](), inputList);

 Obviously, this fails with the error message: "cannot pass type string
 as a function argument".

 I very much would like to avoid empty declaration lines like that of
 `result`.  Is there anyway you folks would suggest?
Untested, but you could try MySt[][string].init. But passing empty AA by value sometimes can be surprising. I'm not sure if it will work. -Steve
Jul 11 2016
parent reply Bahman Movaqar <Bahma BahmanM.com> writes:
On 07/11/2016 06:30 PM, Steven Schveighoffer wrote:
 Untested, but you could try MySt[][string].init.
That did it. Thanks.
 But passing empty AA by value sometimes can be surprising. I'm not sure
 if it will work.
Could you elaborate more? -- Bahman
Jul 11 2016
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/11/2016 07:33 AM, Bahman Movaqar wrote:
 On 07/11/2016 06:30 PM, Steven Schveighoffer wrote:
 Untested, but you could try MySt[][string].init.
That did it. Thanks.
 But passing empty AA by value sometimes can be surprising. I'm not sure
 if it will work.
Could you elaborate more?
Both AAs and slices behave like reference types even when passed by value: When a function adds an element, the argument sees that element as well. This is not the case when the argument is an empty (more correctly, null) AA or slice: void foo(string[int] aa) { aa[1] = "one"; } void main() { string[int] a; foo(a); assert(a is null); // The last result would be different if 'a' were not null // before calling 'foo'. string[int] b; b[0] = "zero"; foo(b); assert(b[0] == "zero"); assert(b[1] == "one"); } Ali P.S. There is std.array.assocArray if you already have a range of tuples at hand: https://dlang.org/phobos/std_array.html#.assocArray P.P.S. There is std.algorithm.fold, which works with range chaining (unlike reduce, which was designed before ranges): https://dlang.org/phobos/std_algorithm_iteration.html#.fold
Jul 11 2016
parent reply Bahman Movaqar <Bahma BahmanM.com> writes:
On 07/11/2016 07:15 PM, Ali Çehreli wrote:
 Both AAs and slices behave like reference types even when passed by
 value: When a function adds an element, the argument sees that element
 as well. This is not the case when the argument is an empty (more
 correctly, null) AA or slice:
 
 void foo(string[int] aa) {
     aa[1] = "one";
 }
 
 void main() {
     string[int] a;
     foo(a);
     assert(a is null);
     // The last result would be different if 'a' were not null
     // before calling 'foo'.
 
     string[int] b;
     b[0] = "zero";
     foo(b);
     assert(b[0] == "zero");
     assert(b[1] == "one");
 }
Now I understand. This is tricky --could introduce hard to find bugs. Is there anyway to make sure it doesn't happen? Such as giving the AA a default empty value on the declaration line --like `string[int] a = []`?
 P.P.S. There is std.algorithm.fold, which works with range chaining
 (unlike reduce, which was designed before ranges):
 
   https://dlang.org/phobos/std_algorithm_iteration.html#.fold
`fold` definitely feels more natural. Thanks. -- Bahman
Jul 11 2016
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
On Tuesday, 12 July 2016 at 03:38:44 UTC, Bahman Movaqar wrote:
 Now I understand.
 This is tricky --could introduce hard to find bugs.  Is there 
 anyway to
 make sure it doesn't happen?  Such as giving the AA a default 
 empty
 value on the declaration line --like `string[int] a = []`?
no. the only thing you can do is to add something to aa and immediately `.clear`
Jul 12 2016
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/12/16 5:42 AM, ketmar wrote:
 On Tuesday, 12 July 2016 at 03:38:44 UTC, Bahman Movaqar wrote:
 Now I understand.
 This is tricky --could introduce hard to find bugs.  Is there anyway to
 make sure it doesn't happen?  Such as giving the AA a default empty
 value on the declaration line --like `string[int] a = []`?
no. the only thing you can do is to add something to aa and immediately `.clear`
There was a suggestion to make .clear (a relatively new feature) actually preallocate if it's currently null, but I didn't want to do allocating in that method (too surprising). I do think it would be nice to have an initializer function that simply allocates the impl. Anyone want to do a PR? basically (strawman name): int[int] aa; aa.initialize; // still has 0 elements, but no longer null. I'm not sure you could make a static initializer method, since AA's are special builtin types. Relative modules needed to be changed are object.d and src/rt/aaA.d -Steve
Jul 12 2016
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
On Tuesday, 12 July 2016 at 12:34:40 UTC, Steven Schveighoffer 
wrote:
 There was a suggestion to make .clear (a relatively new 
 feature) actually preallocate if it's currently null, but I 
 didn't want to do allocating in that method (too surprising). I 
 do think it would be nice to have an initializer function that 
 simply allocates the impl.
q&n patch[1]. it adds template arg to `.clear`, so no code breakage here (old `clear` is still not allocate, but `.clear!true` will). [1] https://issues.dlang.org/show_bug.cgi?id=16269
Jul 12 2016
parent reply cym13 <cpicard openmailbox.org> writes:
On Tuesday, 12 July 2016 at 13:01:20 UTC, ketmar wrote:
 On Tuesday, 12 July 2016 at 12:34:40 UTC, Steven Schveighoffer 
 wrote:
 There was a suggestion to make .clear (a relatively new 
 feature) actually preallocate if it's currently null, but I 
 didn't want to do allocating in that method (too surprising). 
 I do think it would be nice to have an initializer function 
 that simply allocates the impl.
q&n patch[1]. it adds template arg to `.clear`, so no code breakage here (old `clear` is still not allocate, but `.clear!true` will). [1] https://issues.dlang.org/show_bug.cgi?id=16269
I'm with Steven here, that's definitely too surprising, initialization should not be linked to clear in any way.
Jul 12 2016
parent ketmar <ketmar ketmar.no-ip.org> writes:
On Tuesday, 12 July 2016 at 13:56:49 UTC, cym13 wrote:
 I'm with Steven here, that's definitely too surprising, 
 initialization should not be linked to clear in any way.
feel free to change/improve it. for me it is logical, but i'm often found that i am the only one who agrees with my logic. ;-)
Jul 12 2016
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 7/11/16 10:33 AM, Bahman Movaqar wrote:
 On 07/11/2016 06:30 PM, Steven Schveighoffer wrote:
 But passing empty AA by value sometimes can be surprising. I'm not sure
 if it will work.
Could you elaborate more?
An AA initializes on demand. So if you pass by value *after* it has been initialized it behaves like a reference type. But before it's initialized, it's essentially a null pointer. This can cause surprising behavior: foo(int[int] aa) { foreach(i; 0 .. 100) aa[i] = i; } void main() { import std.random; int[int] aa; // initialized as null if(uniform!ubyte < 128) aa[0] = 0; // intialized foo(aa); // at this point, depending on random initialization, either aa is filled or is still null. } -Steve
Jul 11 2016