Jump to page: 1 2
Thread overview
Sorting structs?
Jan 29, 2014
Boomerang
Jan 29, 2014
Benjamin Thaut
Jan 29, 2014
Stanislav Blinov
Jan 29, 2014
Stanislav Blinov
Jan 29, 2014
Boomerang
Jan 29, 2014
Boomerang
Jan 29, 2014
Stanislav Blinov
Jan 29, 2014
Tobias Pankrath
Jan 29, 2014
Stanislav Blinov
Jan 30, 2014
Paul-Andre
Jan 30, 2014
Jesse Phillips
January 29, 2014
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!
January 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
January 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);
January 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).
January 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?
January 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.
January 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 :)
January 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.
January 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:
>>
>>    // Or avoid foreach altogether:
>>    studs.map!((ref const s) => writeln(s.name));
>> }
> Did you test this one? std.algorithm.map is lazy.

Ah yes, sorry, my bad. I forgot to remove foreach during testing and assumed it worked. Facepalm m)
January 30, 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 copies

I am new to D.
Why should someone use "ref const" instead of "in"?
« First   ‹ Prev
1 2