Accept/return as inout in min/max

This commit is contained in:
Eugen Wissner 2018-04-26 08:06:06 +02:00
parent ed5fa91e64
commit 3468d6ea00
1 changed files with 55 additions and 33 deletions

View File

@ -14,10 +14,27 @@
*/ */
module tanya.algorithm.comparison; module tanya.algorithm.comparison;
import tanya.meta.metafunction;
import tanya.meta.trait; import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array; import tanya.range.array;
import tanya.range.primitive; import tanya.range.primitive;
private ref inout(Args[0]) minMax(alias cmp, Args...)(ref inout Args args)
{
auto actual = ((ref inout arg) @trusted => &arg)(args[0]);
foreach (i, arg; args[1 .. $])
{
if (cmp(arg, *actual))
{
actual = ((ref inout arg) @trusted => &arg)(args[i + 1]);
}
}
return *actual;
}
/** /**
* Finds the smallest element in the argument list or a range. * Finds the smallest element in the argument list or a range.
* *
@ -37,27 +54,21 @@ import tanya.range.primitive;
* *
* Returns: The smallest element. * Returns: The smallest element.
*/ */
Args[0] min(Args...)(Args args) inout(Unqual!(Args[0])) min(Args...)(inout Args args)
if (Args.length > 0 && isOrderingComparable!(Args[0]) && allSameType!Args) if (Args.length > 0
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{ {
return min!Args(args); return minMax!((a, b) => a < b)(args);
} }
/// ditto /// ditto
ref Args[0] min(Args...)(ref Args args) ref inout(Unqual!(Args[0])) min(Args...)(ref inout Args args)
if (Args.length > 0 && isOrderingComparable!(Args[0]) && allSameType!Args) if (Args.length > 0
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{ {
auto actual = (() @trusted => &args[0])(); return minMax!((a, b) => a < b)(args);
foreach (arg; args[1 .. $])
{
if (arg < *actual)
{
actual = (() @trusted => &arg)();
}
}
return *actual;
} }
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
@ -105,7 +116,7 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
{ {
assert(min(cast(ubyte[]) []).empty); assert(min(cast(ubyte[]) []).empty);
} }
/** /**
* Finds the largest element in the argument list or a range. * Finds the largest element in the argument list or a range.
* *
@ -125,27 +136,21 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
* *
* Returns: The largest element. * Returns: The largest element.
*/ */
Args[0] max(Args...)(Args args) inout(Unqual!(Args[0])) max(Args...)(inout Args args)
if (Args.length > 0 && isOrderingComparable!(Args[0]) && allSameType!Args) if (Args.length > 0
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{ {
return max!Args(args); return minMax!((a, b) => a > b)(args);
} }
/// ditto /// ditto
ref Args[0] max(Args...)(ref Args args) ref inout(Unqual!(Args[0])) max(Args...)(ref inout Args args)
if (Args.length > 0 && isOrderingComparable!(Args[0]) && allSameType!Args) if (Args.length > 0
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{ {
auto actual = (() @trusted => &args[0])(); return minMax!((a, b) => a > b)(args);
foreach (arg; args[1 .. $])
{
if (arg > *actual)
{
actual = (() @trusted => &arg)();
}
}
return *actual;
} }
@nogc nothrow pure @safe unittest @nogc nothrow pure @safe unittest
@ -193,3 +198,20 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
{ {
assert(max(cast(ubyte[]) []).empty); assert(max(cast(ubyte[]) []).empty);
} }
// min/max compare const and mutable structs.
@nogc nothrow pure @safe unittest
{
static struct S
{
int s;
int opCmp(typeof(this) that) const @nogc nothrow pure @safe
{
return this.s - that.s;
}
}
const s1 = S(1);
assert(min(s1, S(2)).s == 1);
assert(max(s1, S(2)).s == 2);
}