www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Can classes be used as associative array keys?

reply Eric Hanchrow <offby1 blarg.net> writes:
The documentation says they can, but the following code fails the
second assertion:

========================================
    class thing
    {
      int x;
      this (int i) { x = i; }
    }

    int main ()
    {
      thing a = new thing (123);
      thing b = new thing (123);
      int[thing] map1;
      map1[a] = 0;
      map1[b] = 1;

      int[char[]] map2;
      map2["123"] = 0;
      map2["123"] = 1;

      assert (1 == map2.length);
      assert (1 == map1.length);

      return 0;
    }
========================================

Perhaps I need to write a comparison or hashing member for my class,
but the documentation doesn't describe it.  Any ideas how I can get
this to work?

-- 
            |\      _,,,---,,_
      ZZZzz /,`.-'`'    -.  ;-;;,_
           |,4-  ) )-,_. ,\ (  `'-'
          '---''(_/--'  `-'\_) fL
Oct 10 2004
next sibling parent Sjoerd van Leent <svanleent wanadoo.nl> writes:
Eric Hanchrow wrote:
 The documentation says they can, but the following code fails the
 second assertion:
 
 ========================================
     class thing
     {
       int x;
       this (int i) { x = i; }
     }
 
     int main ()
     {
       thing a = new thing (123);
       thing b = new thing (123);
       int[thing] map1;
       map1[a] = 0;
       map1[b] = 1;
 
       int[char[]] map2;
       map2["123"] = 0;
       map2["123"] = 1;
 
       assert (1 == map2.length);
       assert (1 == map1.length);
 
       return 0;
     }
 ========================================
 
 Perhaps I need to write a comparison or hashing member for my class,
 but the documentation doesn't describe it.  Any ideas how I can get
 this to work?
 

Classes are standard by reference. So you have two new "things" made, which are not the same. These are added to map1 as a and b. So there are logically two items within the map. For char[] it is different. You are using it a char as array, which in contrast to a class *does* point to the same item. If you would want to let "thing a" and "thing b" be the same, you should let b point to a, such as: thing a = new thing(123); thing b = a; this would work as you wanted. Regards, Sjoerd
Oct 10 2004
prev sibling parent reply Regan Heath <regan netwin.co.nz> writes:
On Sun, 10 Oct 2004 11:45:51 -0700, Eric Hanchrow <offby1 blarg.net> wrote:
 The documentation says they can, but the following code fails the
 second assertion:

 ========================================
     class thing
     {
       int x;
       this (int i) { x = i; }
     }

     int main ()
     {
       thing a = new thing (123);
       thing b = new thing (123);
       int[thing] map1;
       map1[a] = 0;
       map1[b] = 1;

       int[char[]] map2;
       map2["123"] = 0;
       map2["123"] = 1;

       assert (1 == map2.length);
       assert (1 == map1.length);

       return 0;
     }
 ========================================

 Perhaps I need to write a comparison or hashing member for my class,
 but the documentation doesn't describe it.  Any ideas how I can get
 this to work?

Sjoerd is right about the problem with your example above, 'a' and 'b' above do not compare equal because they are references to different blocks of memory. The default comparrison compares memory location. I thought you could define your own opCmp or opEquals operator in order to make the associative array realise that you intend 'a' and 'b' to be 'the same' but all my attempts have failed... class thing { int x; this (int i) { x = i; } int opCmp(thing rhs) { return x-rhs.x; } int opEquals(thing rhs) { return x==rhs.x; } } perhaps someone else can help us both? Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Oct 10 2004
parent reply Eric Hanchrow <offby1 blarg.net> writes:
 "Regan" == Regan Heath writes:





Regan> I thought you could define your own opCmp or opEquals Regan> operator in order to make the associative array realise Regan> that you intend 'a' and b' to be 'the same' but all my Regan> attempts have failed... Same here. I'm sure there's a way to do it, or at least, that Walter intends there eventually to be such a way. -- Yahoo uses Javascript in a few places, though not many. I once asked someone there how this worked out, and he said "they ended up learning a lot about different browser versions." --Paul Graham (http://www.paulgraham.com/road.html)
Oct 10 2004
parent reply Regan Heath <regan netwin.co.nz> writes:
On Sun, 10 Oct 2004 15:47:13 -0700, Eric Hanchrow <offby1 blarg.net> wrote:

 "Regan" == Regan Heath writes:





Regan> I thought you could define your own opCmp or opEquals Regan> operator in order to make the associative array realise Regan> that you intend 'a' and b' to be 'the same' but all my Regan> attempts have failed... Same here. I'm sure there's a way to do it, or at least, that Walter intends there eventually to be such a way.

I found (after looking in \dmd\src\phobos\internal\object.d) the 'uint toHash()' method, adding one like so: class thing { int x; this (int i) { x = i; } int opCmp(Object p) { thing rhs = cast(thing)p; printf("opCmp %d,%d\n",x,rhs.x); return x-rhs.x; } int opEquals(Object p) { thing rhs = cast(thing)p; printf("opEquals %d,%d\n",x,rhs.x); return x==rhs.x; } uint toHash() { printf("toHash %d\n",x); return x; } } makes it work as desired :) Notice I had to change "opCmp(thing .." to "opCmp(Object .." as well. It would be nice if that was not necessary, after all, the AA does know what type is involved. Perhaps that would require a cast internally so isn't done for efficiency reasons? Perhaps it's done so as you have to handle it being compared to all objects, if that is the case, how do you return not comparable? eg. int opCmp(Object o) { Foo f; Bar b; f = cast(Foo)o; if (f) return compare_with_foo(); b = cast(Bar)o; if (b) return compare_with_bar(); //how do we return 'not comparable?' return 0; } Regan -- Using M2, Opera's revolutionary e-mail client: http://www.opera.com/m2/
Oct 10 2004
next sibling parent Derek Parnell <derek psych.ward> writes:
On Mon, 11 Oct 2004 12:24:49 +1300, Regan Heath wrote:


[snip]

    //how do we return 'not comparable?'

Throw an exception, maybe? -- Derek Melbourne, Australia 11/10/2004 9:37:18 AM
Oct 10 2004
prev sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
Regan Heath wrote:

<snip>
 Perhaps it's done so as you have to handle it being compared to all 
 objects, if that is the case, how do you return not comparable?

You can usually get away with assuming that the RHS is of compatible type. If the cast fails, it will be a null reference and cause an AV. Of course, if you've got mutually comparable classes to be considered separately, that's another matter.... But several of us agree that it's on the stupid side. D/26144 digitalmars.D/10558 Stewart.
Oct 11 2004