November 05, 2014
On Wednesday, 5 November 2014 at 16:54:38 UTC, Marc Schütz wrote:
> My idea was something along these lines (untested):
>
>     template extractorFun(alias extractor) {
>         static if(is(typeof(extractor) : string)) {
>             auto ref extractorFun(T)(auto ref T a) {
>                 mixin("with(a) { return " ~ extractor ~ "; }");
>             }
>         } else {
>             alias extractorFun = extractor;
>         }
>     }
>
>     alias fn = extractorFun!extractor;
>     r.sort!((a, b) => (fn(a) < fn(b)));

Thanks, I'll use this!

Still, is it even possible to extend extractor to become variadic or do I have to write a number of explicit overloads

1: void sortBy(alias xtr, R)(R r)
2: void sortBy(alias xtrA, alias xtrB, R)(R r)
3: void sortBy(alias xtrA, alias xtrB, alias xtrC, R)(R r)
etc

?
November 05, 2014
On Wednesday, 5 November 2014 at 20:50:00 UTC, Nordlöw wrote:
> On Wednesday, 5 November 2014 at 16:54:38 UTC, Marc Schütz wrote:
>> My idea was something along these lines (untested):
>>
>>    template extractorFun(alias extractor) {
>>        static if(is(typeof(extractor) : string)) {
>>            auto ref extractorFun(T)(auto ref T a) {
>>                mixin("with(a) { return " ~ extractor ~ "; }");
>>            }
>>        } else {
>>            alias extractorFun = extractor;
>>        }
>>    }
>>
>>    alias fn = extractorFun!extractor;
>>    r.sort!((a, b) => (fn(a) < fn(b)));
>
> Thanks, I'll use this!
>
> Still, is it even possible to extend extractor to become variadic or do I have to write a number of explicit overloads
>
> 1: void sortBy(alias xtr, R)(R r)
> 2: void sortBy(alias xtrA, alias xtrB, R)(R r)
> 3: void sortBy(alias xtrA, alias xtrB, alias xtrC, R)(R r)
> etc
>
> ?

Again untested:

private alias makePredicate(alias xtr) =
    (a, b) => (extractorFun!xtr(a) < extractorFun!xtr(b));

auto sortBy(extractors..., R)(R r) {
    alias preds = staticMap!(makePredicate, extractors);
    return r.sort!preds;
}

(Variadic template parameters also accept aliases.)
November 05, 2014
On Wednesday, 5 November 2014 at 16:54:38 UTC, Marc Schütz wrote:
> On Wednesday, 5 November 2014 at 16:07:40 UTC, Nordlöw wrote:
>> On Wednesday, 5 November 2014 at 14:36:11 UTC, Marc Schütz wrote:
>>> It's intuitive and concise. Plus, Ruby uses `sort` and `sort_by` for the same functionality, exactly in parallel, so it will be familiar to many users.
>>
>> Here's my first working but primitive version.
>>
>> https://github.com/nordlow/justd/blob/master/sort_ex.d#L15
>>
>> How do I extend it to support
>>
>> - variadic number of extractors
>> - sortBy("x")
>
> My idea was something along these lines (untested):
>
>     template extractorFun(alias extractor) {
>         static if(is(typeof(extractor) : string)) {
>             auto ref extractorFun(T)(auto ref T a) {
>                 mixin("with(a) { return " ~ extractor ~ "; }");
>             }
>         } else {
>             alias extractorFun = extractor;
>         }
>     }
>
>     alias fn = extractorFun!extractor;
>     r.sort!((a, b) => (fn(a) < fn(b)));

Works! Thanks!

One thing: I tried to add support for integer based member indexing via tupleof at

https://github.com/nordlow/justd/blob/master/sort_ex.d#L21

but it errors as

sort_ex.d(42,46): Error: function expected before (), not 0 of type int
sort_ex.d(43,46): Error: function expected before (), not 0 of type int
sort_ex.d(42,6):        instantiated from here: sort!((a, b) => extractorFun!extractor(a) < extractorFun!extractor(b), cast(SwapStrategy)0, X[])
sort_ex.d(80,6):        instantiated from here: sortBy!(0, X[])
/home/per/opt/x86_64-unknown-linux-gnu/dmd/linux/bin64/src/phobos/std/algorithm.d(10266,9): Error: static assert  "Invalid predicate passed to sort: __lambda2"
sort_ex.d(42,6):        instantiated from here: sort!((a, b) => extractorFun!extractor(a) < extractorFun!extractor(b), cast(SwapStrategy)0, X[])
sort_ex.d(80,6):        instantiated from here: sortBy!(0, X[])

when I uncomment the last three tests. What's wrong?
November 06, 2014
On Wednesday, 5 November 2014 at 23:40:23 UTC, Nordlöw wrote:
> when I uncomment the last three tests. What's wrong?

Solved it :)
November 06, 2014
On Wednesday, 5 November 2014 at 21:29:20 UTC, Marc Schütz wrote:
> Again untested:
>
> private alias makePredicate(alias xtr) =
>     (a, b) => (extractorFun!xtr(a) < extractorFun!xtr(b));

This errors as

sort_ex.d(35,43): Error: basic type expected, not (
sort_ex.d(35,43): Error: function declaration without return type. (Note that constructors are always named 'this')
sort_ex.d(35,50): Error: semicolon expected to close alias declaration
sort_ex.d(35,50): Error: Declaration expected, not '=>'

> auto sortBy(extractors..., R)(R r) {
>     alias preds = staticMap!(makePredicate, extractors);
>     return r.sort!preds;
> }

and this as

sort_ex.d(37,6): Error: template sort_ex.sortBy(xtors..., R)(R r) template tuple parameter must be last one
November 06, 2014
On Thursday, 6 November 2014 at 09:13:07 UTC, Nordlöw wrote:
> On Wednesday, 5 November 2014 at 21:29:20 UTC, Marc Schütz wrote:
>> Again untested:
>>
>> private alias makePredicate(alias xtr) =
>>    (a, b) => (extractorFun!xtr(a) < extractorFun!xtr(b));
>
> This errors as
>
> sort_ex.d(35,43): Error: basic type expected, not (
> sort_ex.d(35,43): Error: function declaration without return type. (Note that constructors are always named 'this')
> sort_ex.d(35,50): Error: semicolon expected to close alias declaration
> sort_ex.d(35,50): Error: Declaration expected, not '=>'
>

It probably needs to be spelled out as function/delegate literal (sorry, don't have much time now).

>> auto sortBy(extractors..., R)(R r) {
>>    alias preds = staticMap!(makePredicate, extractors);
>>    return r.sort!preds;
>> }
>
> and this as
>
> sort_ex.d(37,6): Error: template sort_ex.sortBy(xtors..., R)(R r) template tuple parameter must be last one

Try nesting the templates, then the tuple can come first:

    template sortBy(extractors...) {
        auto sortBy(R)(R r) {
            // ...
        }
    }

This should then be usable as `sortBy!("x", "y")(range)`.
November 09, 2014
On 11/5/14 12:18 PM, "Marc =?UTF-8?B?U2Now7x0eiI=?= <schuetzm@gmx.net>" wrote:
> On Wednesday, 5 November 2014 at 00:34:54 UTC, Meta wrote:
>> On Wednesday, 5 November 2014 at 00:32:32 UTC, Nordlöw wrote:
>>> Has there been any proposals to add a sort-wrapper, say sortBy,
>>>
>>> in cases such as
>>>
>>>    struct X { double x, y, z; }
>>>    auto r = new X[3];
>>>
>>> used as
>>>
>>>    r.sortBy!("x", "y")
>>>
>>> sorting r by value of "x" then "y".
>>>
>>> If not and anybody is interest I could write one and make PR in
>>> std.algorithm.
>>
>> I think you're looking for multiSort.
>>
>> http://dlang.org/phobos/std_algorithm.html#.multiSort
>
> That's not the same, it requires to specify a comparison function.
> Nordlöw wants to specify an attribute.
>
> This could also be an arbitrary expression, of course:
>
>      r.sortBy!"x*x + y*y + z*z"
>
> The above could be implemented using `with` and
> `std.functional.unaryFun`. Alternatively, a lambda could be used:
>
>      r.sortBy!(a => a.norm);

sortBy!(expr) is equivalent with sort!(expr_a < expr_b) so there's no additional power to it. However, it does make some sorting criteria easier to write. A while ago I took a related diff pretty far but in the end decided to discard it because it didn't add sufficient value.

Andrei

November 09, 2014
On Sunday, 9 November 2014 at 08:12:37 UTC, Andrei Alexandrescu wrote:
> sortBy!(expr) is equivalent with sort!(expr_a < expr_b) so there's no additional power to it. However, it does make some sorting criteria easier to write. A while ago I took a related diff pretty far but in the end decided to discard it because it didn't add sufficient value.

What do you mean by sufficient value?

Ruby has sort_by. I believe D would when possible should be a superset of the union of powers available in other languages to maximize the ease of porting code *to* D and the pleasure for programmers to switch to D. I'll try to make this elegant and useful and use it myself for a while. If it works for me I'll do a PR still. Ok?
November 09, 2014
Nordlöw:

> What do you mean by sufficient value?

Every thing you add to Phobos std.algorithm&std.range increases the amount of things D programmers have to understand and keep in _active memory_. So you have to balance the desire to add specialized functionality with the desire to keep that complexity low. And I think adding sortBy isn't a good idea.

If you really want to do something like that, I think it's better to add something more like Python "operator.attrgetter" (https://docs.python.org/2/library/operator.html ), that can be used with the standard sort:

items.sort!(fieldGetter("field1", "field2"));

Bye,
bearophile
November 10, 2014
On 11/9/14 4:49 AM, "Nordlöw" wrote:
> On Sunday, 9 November 2014 at 08:12:37 UTC, Andrei Alexandrescu wrote:
>> sortBy!(expr) is equivalent with sort!(expr_a < expr_b) so there's no
>> additional power to it. However, it does make some sorting criteria
>> easier to write. A while ago I took a related diff pretty far but in
>> the end decided to discard it because it didn't add sufficient value.
>
> What do you mean by sufficient value?

It seemed to me just another way of doing the same things.

> Ruby has sort_by. I believe D would when possible should be a superset
> of the union of powers available in other languages to maximize the ease
> of porting code *to* D and the pleasure for programmers to switch to D.

Sadly things don't work that way - e.g. Phobos1 took a bunch of string functions from Ruby and Python, to no notable effect.

> I'll try to make this elegant and useful and use it myself for a while.
> If it works for me I'll do a PR still. Ok?

That's a better approach, thanks. A few compelling examples would help.


Andrei