Hacker News new | ask | show | jobs
by aub3bhat 3105 days ago
Please show me C++ equivalent of

    sorted([(k.weight, k.name) for k in somelist], reverse=True)
6 comments

I'll bite.

  std::vector<std::pair<float, std::string>> in_order;
  std::transform(data.begin(), data.end(), std::back_inserter(in_order),
                 [](auto& item) {return std::make_pair(item.weight, item.name);});
  std::sort(in_order.begin(), in_order.end(), std::greater<>());
While I won't claim it to be as elegant as Python, it doesn't seem too ugly. Does anybody have an idea about how `auto` could be utilized to avoid the long vector<pair...> type declaration?

For the adventurous: https://repl.it/repls/RepentantGentleKudu

Templates, auto, and type toys can give you some serious build-your-own syntactic sugar: https://repl.it/repls/PleasingLovingQueenslandheeler

Pulling things out of tuples isn't great, though.

Note: my C++ is extremely rusty.

Right now, I think that's approximately,

    vector<tuple<int, string>> output;
    transform(
        somelist.begin(), somelist.end(),
        back_inserter(output),
        [](const auto &f) { return make_tuple(f.weight, f.name); }
    );
    sort(output.rbegin(), output.rend());
Ranges, I believe, would reduce this a lot, possibly even to a single line. If I am reading the docs on it correctly, something like,

    vector<auto>(somelist | view::transform([](const auto &f) { return make_tuple(f.weight, f.name); })) | action::sort;
I think.

Two notes, however:

1. I feel like most of the desire for a static language is to know what type something is. Is C++ exactly as brief as Python? No, as I think you've demonstrated. But I think you're a lot more likely to know the type of something. Rarely do I think I find that Python has annotations, and annotations can be wrong.

2. C++ is, in general, I feel, much more explicit about where copies occur. I elided one of the copies in your example, opting instead for an in-place sort (but this is trivial to fix in the Python).

> 1. I feel like most of the desire for a static language is to know what type something is. Is C++ exactly as brief as Python? No, as I think you've demonstrated. But I think you're a lot more likely to know the type of something.

Sure, but you don't have to choose between them, there are plenty of languages where you can have both Pythonic terseness and full type safety. E.g. Scala:

    (for {k <- somelist} yield (k.weight, k.name)).sorted.reverse
Many other strongly typed (ML-like) functional languages are similar.
> vector<auto>

Really? O.o How could this possibly work?

Oh, that was probably a typo (it was late); replace that with a concrete type.
Here (http://rextester.com/QUIK26485):

  somelist | transformed([](auto &&k) { return make_pair(k.weight, k.name); }) | sorted | reversed;
Easy:

    std::vector<P> people = { P{"jane", 47}, P{"mary", 71}, P{"john", 65} };

    ranges::sort(people, [](auto& x, auto& y){ return x.weight > y.weight; });
Compile this gist with `c++ -std=c++1z -I range-v3/include`:

https://gist.github.com/cieplak/dcd587c67d989768900e4110e776...

What if two weights are equal, the python code will also sort by name as well ;)
No idea about C++ but in C# seems quite more readable (and flexible)

    someList.OrderByDescending(x => x.weight).ThenByDescending(x => x.Name);
It's quite similar to expressing the concept in English, certainly more than using list comprehension in Python.

And how would you order it in Python by ascending on the first field and descending on the second using list comprehension?

That's not the same thing, you've sorted a list of objects, we're looking for a list of tuples of `weight, name`)

In answer to your question though,

    sorted(((k.weight, k.name) for k in some_list), key=lambda x: (-x[0], x[1]), reverse=True)
appears to work. This does use a non-obvious trick, but being more explicit is a smidge difficult, since the key function is called only n times, as opposed to O(nlogn) in the C# example.

Alternatively, you can use

    sorted(sorted(((k.weight, k.name) for k in some_list), key=lambda x: x[1], reverse=True), key=x[0])
Which is more like the original example, and if you're doing it in place, you get

    outs = [k.weight, k.name) for k in some_list]
    outs.sort(key=lambda x: x[1], reverse=True)
    outs.sort(key=lambda x: x[0])
Python's builtin sort is timsort, so despite sorting the list twice, this will still run in approximately NlogN comparisons, not 2NlogN.

You could also manually define a custom comparator, ie

   lambda s, o: (s[0] > o[0] * 10 + s[1] < o[1])
and pass it to `functools.cmp_to_key`.
You could implement the following with a decent bit of work (declaring k and reversed to be variables of special, hand-written/macro-generated types with overloaded operator, and operator=).

sorted( (k.weight, k.name).for_(k).in(somelist), reverse=true));

You would never be able to get that past code review, however.

Sure, but at that point you've just reimplemented python in macros ;)

The other thing to note is that

    sorted([(k.weight, k.name) for k in somelist], reverse=True)
is essentially already typechecked:

    def biggest_ks(ks: K):
        return sorted([(k.weight, k.name) for k in ks], reverse=True)
The above code now has all the same type guarantees as your c++, actually maybe more since the macros you use are going to be...uhhh, mysterious.
The macro would only be used to generate k for convenience. It could be all implemented in straight, macro-less C++98 in O(minutes).

My point was just that you can implement almost whatever you want (even without macros, they'll just expand the design space).

I don't see how you could implement the (k.attr).for_(k) part. That's essentially an assignment, a macro could maybe convert it to a lambda, but I don't see how it would be done macro-free.