www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Custom opCmp for struct array sort

reply New <d-devel here.com> writes:
Hello,

I am new with D and am stuck with something I hope somebody can help me with it.
I am trying sort an array of paths which can take the two following forms:

/usr                      ---> normal path
/bin=/other/bin     ---> a symlink

When I sort these paths, in the case of symlinks I want to ignore the right
hand side and I just use the path before the "=" sign.

The array docs for DMD 2.0, say that can create a structure and create a opCmp
function to cutomise the way sort works. I have tried this but calling .sort
does not seem to pick-up my custom opCmp.

Any help would be very appreciated. I am including the code below

Thanks a lot
--------------------------------------------------------------------

struct Path {
  invariant(char) [] thePath;

  int opCmp(Path p) {
    int pos;
    invariant(char) [] a;
    invariant(char) [] b;
    if( (pos=find(this.thePath, RegExp("="))) > -1 ) {
      a = this.thePath[0 .. pos];
    } else {
      a = this.thePath;
    }
    if( (pos=find(p.thePath, RegExp("="))) > -1 ) {
      b = p.thePath[0 .. pos];
    } else {
      b = p.thePath;
    }
    int cosa = std.string.cmp(a, b);
    writefln( a, " comp ", b, "=", cosa);
    return std.string.cmp(a, b);
  }
}

int main(char[][] args) {
  char[][][Path] contents;

  Path one;
  one.thePath = "/002=/other_dir";
  contents[one] = cast(char[][])["aa","bb","cc","dd"];

  Path two;
  two.thePath = "/001";
  contents[two] = cast(char[][])["aa","bb","cc","dd"];

  foreach(item; contents.keys.sort) {
    writefln( item.thePath );
  }

  return 0;
}
Nov 28 2008
next sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
New:
 When I sort these paths, in the case of symlinks I want to ignore the right
hand side and I just use the path before the "=" sign.
This may work (D1), I have cleaned up your code some: import std.string: find, cmp; import std.stdio: writefln; struct Path { string thePath; int opCmp(Path other) { int pos; string a, b; pos = find(this.thePath, "="); if (pos > -1) a = this.thePath[0 .. pos]; else a = this.thePath; pos = find(other.thePath, "="); if (pos > -1) b = other.thePath[0 .. pos]; else b = other.thePath; return cmp(a, b); } } void main() { string[][Path] contents = [ Path("/002=/other_dir"): ["aa","bb","cc","dd"], Path("/001"): ["aa","bb","cc","dd"], Path("/002=/hello") : ["aa","bb","cc","dd"] ]; foreach (item; contents.keys.sort) writefln(item.thePath); } Note that you may not need it, because the part before = is on the left, so just sorting the strings may give you the order you need, without using any opCmp. For other similar questions the D.learn newsgroup is fitter. Bye, bearophile
Nov 28 2008
parent reply New <d-devel here.com> writes:
Hello,

Thanks for cleaning up the code, I was getting a bit annoyed with the all the
invariants, but I did not know how to get rid of them.

I tried your version but still the opCmp is not called  :(

I will try on D.learn

thanks very much



bearophile Wrote:

 New:
 When I sort these paths, in the case of symlinks I want to ignore the right
hand side and I just use the path before the "=" sign.
This may work (D1), I have cleaned up your code some: import std.string: find, cmp; import std.stdio: writefln; struct Path { string thePath; int opCmp(Path other) { int pos; string a, b; pos = find(this.thePath, "="); if (pos > -1) a = this.thePath[0 .. pos]; else a = this.thePath; pos = find(other.thePath, "="); if (pos > -1) b = other.thePath[0 .. pos]; else b = other.thePath; return cmp(a, b); } } void main() { string[][Path] contents = [ Path("/002=/other_dir"): ["aa","bb","cc","dd"], Path("/001"): ["aa","bb","cc","dd"], Path("/002=/hello") : ["aa","bb","cc","dd"] ]; foreach (item; contents.keys.sort) writefln(item.thePath); } Note that you may not need it, because the part before = is on the left, so just sorting the strings may give you the order you need, without using any opCmp. For other similar questions the D.learn newsgroup is fitter. Bye, bearophile
Nov 28 2008
parent reply bearophile <bearophileHUGS lycos.com> writes:
New Wrote:

 Thanks for cleaning up the code, I was getting a bit annoyed with the all the
invariants, but I did not know how to get rid of them.
Note that my code is D1.
 I tried your version but still the opCmp is not called  :(
I don't agree, it's called: http://codepad.org/FLVu8gqQ Bye, bearophile
Nov 28 2008
parent reply New <d-devel here.com> writes:
I see, sorry about that, I missed the D1 bit in your message and tested on D2,
what would I need to do to make it run on D2?

Is D2 production ready?, is it better to use D1?

thanks


bearophile Wrote:

 New Wrote:
 
 Thanks for cleaning up the code, I was getting a bit annoyed with the all the
invariants, but I did not know how to get rid of them.
Note that my code is D1.
 I tried your version but still the opCmp is not called  :(
I don't agree, it's called: http://codepad.org/FLVu8gqQ Bye, bearophile
Nov 28 2008
parent reply bearophile <bearophileHUGS lycos.com> writes:
New Wrote:
 what would I need to do to make it run on D2?
I don't know, sorry :-)
 Is D2 production ready?
No, surely D2 isn't "production ready", it's alpha stage. D1 is barely like that, despite being a stable beta.
is it better to use D1?
D2 is now mostly for testing, I presume. But you are free to use it, it works. Bye, bearophile
Nov 28 2008
parent BCS <ao pathlink.com> writes:
Reply to bearophile,

 New Wrote:

 is it better to use D1?
 
D2 is now mostly for testing, I presume. But you are free to use it, it works.
I'd say it more of a 1.0 release version. Heck windows was a piece of junk for v1 and v2.</joke>
Nov 28 2008
prev sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"New" wrote
 Hello,

 I am new with D and am stuck with something I hope somebody can help me 
 with it.
 I am trying sort an array of paths which can take the two following forms:

 /usr                      ---> normal path
 /bin=/other/bin     ---> a symlink

 When I sort these paths, in the case of symlinks I want to ignore the 
 right hand side and I just use the path before the "=" sign.

 The array docs for DMD 2.0, say that can create a structure and create a 
 opCmp function to cutomise the way sort works. I have tried this but 
 calling .sort does not seem to pick-up my custom opCmp.

 Any help would be very appreciated. I am including the code below

 Thanks a lot
 --------------------------------------------------------------------

 struct Path {
  invariant(char) [] thePath;

  int opCmp(Path p) {
    int pos;
    invariant(char) [] a;
    invariant(char) [] b;
    if( (pos=find(this.thePath, RegExp("="))) > -1 ) {
      a = this.thePath[0 .. pos];
    } else {
      a = this.thePath;
    }
    if( (pos=find(p.thePath, RegExp("="))) > -1 ) {
      b = p.thePath[0 .. pos];
    } else {
      b = p.thePath;
    }
    int cosa = std.string.cmp(a, b);
    writefln( a, " comp ", b, "=", cosa);
    return std.string.cmp(a, b);
  }
 }

 int main(char[][] args) {
  char[][][Path] contents;

  Path one;
  one.thePath = "/002=/other_dir";
  contents[one] = cast(char[][])["aa","bb","cc","dd"];

  Path two;
  two.thePath = "/001";
  contents[two] = cast(char[][])["aa","bb","cc","dd"];

  foreach(item; contents.keys.sort) {
    writefln( item.thePath );
  }

  return 0;
 }
The problem is the opCmp function signature. One of the undocumented (as far as I know) features of the compiler is that for a struct, if there is an opCmp of a certain signature, the compiler saves a function pointer to that opCmp in that struct's typeinfo as 'the' opCmp to use when comparing two of those structs. This is used in associative arrays and with sort. the appropriate signature for D2 (as of 2.019) is: struct S { int opCmp(const S *s) const; } So in order for your code to be used during sort in D 2, you need to change your signature to:
  int opCmp(const Path * p) const {
Then it should work. If it's different in D 2.021 (and it probably should be given the advent of 'SafeD' requiring no pointers), then I don't know what it might be, but I'd guess you'd replace 'const Path *p' with 'ref const Path p'. I think these 'special' features should be documented, probably in the operators page. I'll file a bugzilla request. -Steve
Dec 01 2008