digitalmars.D.learn - Sorting structs?
- Boomerang (14/14) Jan 29 2014 Hello everybody. I'm trying to learn D by solving simple
- Benjamin Thaut (22/36) Jan 29 2014 You can either overload the compare operator for the struct
- Stanislav Blinov (13/15) Jan 29 2014 This can be solved without sorting in reverse or overloading
- Stanislav Blinov (8/8) Jan 29 2014 It's also worth noting that you can avoid passing structs into
- Boomerang (4/4) Jan 29 2014 Thanks for the help so far.
- Boomerang (4/4) Jan 29 2014 Also sorry for double posting but I'm confused how the sorting
- Stanislav Blinov (67/71) Jan 29 2014 On Wednesday, 29 January 2014 at 10:30:06 UTC, Boomerang wrote:
- Tobias Pankrath (3/6) Jan 29 2014 Did you test this one? std.algorithm.map is lazy.
- Stanislav Blinov (4/11) Jan 29 2014 Ah yes, sorry, my bad. I forgot to remove foreach during testing
- Paul-Andre (4/7) Jan 29 2014 I am new to D.
- Jesse Phillips (3/10) Jan 29 2014 in is the same as 'scope const' (scope checks aren't implemented)
Hello everybody. I'm trying to learn D by solving simple exercises. The current exercise: read names and grades of students, then print the names of students in descending order of their grades. In C++, I would define a Student struct, overload the operators: bool operator<(const Student&, const Student&) std::istream& operator>>(std::istream&, Student&) Then I'd store the Students in let's say a std::vector, sort it in reverse, then print it accordingly. But I don't know how to do things in D. I'm reading Cehreli's book, so far I've only finished its first trinity (just before struct). I would appreciate it if you explained your approach and gave source code illustrating it. Thanks!
Jan 29 2014
Am 29.01.2014 10:33, schrieb Boomerang:Hello everybody. I'm trying to learn D by solving simple exercises. The current exercise: read names and grades of students, then print the names of students in descending order of their grades. In C++, I would define a Student struct, overload the operators: bool operator<(const Student&, const Student&) std::istream& operator>>(std::istream&, Student&) Then I'd store the Students in let's say a std::vector, sort it in reverse, then print it accordingly. But I don't know how to do things in D. I'm reading Cehreli's book, so far I've only finished its first trinity (just before struct). I would appreciate it if you explained your approach and gave source code illustrating it. Thanks!You can either overload the compare operator for the struct struct bla { int i; int opCmp(ref const(bla) rh) { if(this.i < rh.i) return -1; if(this.i > rh.i) return 1; return 0; } } or you could just pass a different compare less expression to sort bla[] arrayToSort; sort!"a.i < b.i"(arrayToSort); You can also pass a lambda or any function as compare less function to sort: sort!((a,b){return a.i < b.i;})(arrayToSort); Kind Regards Benjamin Thaut
Jan 29 2014
On Wednesday, 29 January 2014 at 09:33:50 UTC, Boomerang wrote:I would appreciate it if you explained your approach and gave source code illustrating it. Thanks!This can be solved without sorting in reverse or overloading anything in C++ (with a predicate version of sort() algorithm), and in D too. Take a look at this code: http://dpaste.dzfl.pl/2d0dd6f953dc the std.algorithm.sort() takes an optional predicate (as a template argument) to resolve the order of elements. In this case, it is specified as an anonymous function (a,b) => a.grade > b.grade You could alternatively specify it as a string: sort!"a.grade > b.grade"(students); or like this: sort!q{a.grade > b.grade}(students);
Jan 29 2014
It's also worth noting that you can avoid passing structs into predicate function by value, and instead pass them by reference: sort!((ref const a, ref const b) => a.grade > b.grade)(students); This is useful for "heavy" structs to save on performance. E.g. in this case, Student.sizeof is 24 bytes on 64 bit machine. Passing by reference will only need to copy 16 bytes (i.e. size of two pointers), whereas passing by value will incur copying 48 bytes (two structs).
Jan 29 2014
Thanks for the help so far. My current code is here: http://dpaste.dzfl.pl/b9acff399649 Are there problems with my code, or do you have suggestions to improve it?
Jan 29 2014
Also sorry for double posting but I'm confused how the sorting works when we only specify to be sorted by grade. I think in C++, disregarding other data members can lead to loss of data in things such as std::set.
Jan 29 2014
On Wednesday, 29 January 2014 at 10:30:06 UTC, Boomerang wrote: I'll paste here with comments: import std.algorithm; import std.stdio; import std.array; void main() { struct Student { string name; float grade; } Student[] studs; writeln("Enter student data. Reading stops when student name is empty."); while (true) { // Calls to readf() may throw an exception // if user inputs invalid data. // You may want to consider accounting // for that. typeof(Student.name) name; write("Student name: "); readf("%s\n", &name); // Don't burden users with typing STOP :) if (name.empty) break; typeof(Student.grade) grade; write("Student grade: "); import std.exception; import std.conv : ConvException; // EXAMPLE: catch all ConvExceptions // to persuade user to input a number :) while(collectException!ConvException(readf("%s\n", &grade))) { // Conversion failed but there's still data in // stdin. We get rid of it: readln; write("Please enter NUMERIC student grade: "); } // Create Student only when necessary // (a good compiler will optimize that // to avoid copying): studs ~= Student(name, grade); } // Use anonymous function with explicit // parameter storage class specifier; // Also Use UFCS studs.sort!((ref const a, ref const b) => a.grade > b.grade); // You can omit type name in foreach; // also it's worth using ref const to avoid // unnecessary copies foreach (ref const s; studs) writeln(s.name); // Or avoid foreach altogether: studs.map!((ref const s) => writeln(s.name)); }Also sorry for double posting but I'm confused how the sorting works when we only specify to be sorted by grade. I think in C++, disregarding other data members can lead to loss of data in things such as std::set.Yes and no. In fact, std::set is by default instantiated with an std::less predicate. But std::set is intended to store unique elements. So if you try to insert an element that std::set already considers to exist (it uses the predicate to determine that), it doesn't insert it. But you must understand that despite being generic, algorithms and containers still serve particular purposes. You wouldn't store students in a set unless you had some way to uniquely identify them :)
Jan 29 2014
On Wednesday, 29 January 2014 at 11:24:54 UTC, Stanislav Blinov wrote:// Or avoid foreach altogether: studs.map!((ref const s) => writeln(s.name)); }Did you test this one? std.algorithm.map is lazy.
Jan 29 2014
On Wednesday, 29 January 2014 at 12:44:37 UTC, Tobias Pankrath wrote:On Wednesday, 29 January 2014 at 11:24:54 UTC, Stanislav Blinov wrote:Ah yes, sorry, my bad. I forgot to remove foreach during testing and assumed it worked. Facepalm m)// Or avoid foreach altogether: studs.map!((ref const s) => writeln(s.name)); }Did you test this one? std.algorithm.map is lazy.
Jan 29 2014
On Wednesday, 29 January 2014 at 11:24:54 UTC, Stanislav Blinov wrote:On Wednesday, 29 January 2014 at 10:30:06 UTC, Boomerang wrote: // also it's worth using ref const to avoid // unnecessary copiesI am new to D. Why should someone use "ref const" instead of "in"?
Jan 29 2014
On Thursday, 30 January 2014 at 03:00:43 UTC, Paul-Andre wrote:On Wednesday, 29 January 2014 at 11:24:54 UTC, Stanislav Blinov wrote:in is the same as 'scope const' (scope checks aren't implemented) So you be relying on the optimizer to not copy.On Wednesday, 29 January 2014 at 10:30:06 UTC, Boomerang wrote: // also it's worth using ref const to avoid // unnecessary copiesI am new to D. Why should someone use "ref const" instead of "in"?
Jan 29 2014