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;
import tanya.meta.metafunction;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range.array;
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.
*
@ -37,27 +54,21 @@ import tanya.range.primitive;
*
* Returns: The smallest element.
*/
Args[0] min(Args...)(Args args)
if (Args.length > 0 && isOrderingComparable!(Args[0]) && allSameType!Args)
inout(Unqual!(Args[0])) min(Args...)(inout Args args)
if (Args.length > 0
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{
return min!Args(args);
return minMax!((a, b) => a < b)(args);
}
/// ditto
ref Args[0] min(Args...)(ref Args args)
if (Args.length > 0 && isOrderingComparable!(Args[0]) && allSameType!Args)
ref inout(Unqual!(Args[0])) min(Args...)(ref inout Args args)
if (Args.length > 0
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{
auto actual = (() @trusted => &args[0])();
foreach (arg; args[1 .. $])
{
if (arg < *actual)
{
actual = (() @trusted => &arg)();
}
}
return *actual;
return minMax!((a, b) => a < b)(args);
}
@nogc nothrow pure @safe unittest
@ -105,7 +116,7 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
{
assert(min(cast(ubyte[]) []).empty);
}
/**
* 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.
*/
Args[0] max(Args...)(Args args)
if (Args.length > 0 && isOrderingComparable!(Args[0]) && allSameType!Args)
inout(Unqual!(Args[0])) max(Args...)(inout Args args)
if (Args.length > 0
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{
return max!Args(args);
return minMax!((a, b) => a > b)(args);
}
/// ditto
ref Args[0] max(Args...)(ref Args args)
if (Args.length > 0 && isOrderingComparable!(Args[0]) && allSameType!Args)
ref inout(Unqual!(Args[0])) max(Args...)(ref inout Args args)
if (Args.length > 0
&& isOrderingComparable!(Args[0])
&& allSameType!(Map!(Unqual, Args)))
{
auto actual = (() @trusted => &args[0])();
foreach (arg; args[1 .. $])
{
if (arg > *actual)
{
actual = (() @trusted => &arg)();
}
}
return *actual;
return minMax!((a, b) => a > b)(args);
}
@nogc nothrow pure @safe unittest
@ -193,3 +198,20 @@ if (isForwardRange!Range && isOrderingComparable!(ElementType!Range))
{
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);
}