Variadic isSameLength

This commit is contained in:
Andrei Alexandrescu 2021-10-23 08:23:21 -04:00
parent 4ded9c6796
commit 0efda76c67

View file

@ -2031,50 +2031,91 @@ auto predSwitch(alias pred = "a == b", T, R ...)(T switchExpression, lazy R choi
} }
/** /**
Checks if the two ranges have the same number of elements. This function is Checks if two or more ranges have the same number of elements. This function is
optimized to always take advantage of the `length` member of either range optimized to always take advantage of the `length` member of either range
if it exists. if it exists.
If both ranges have a length member, this function is $(BIGOH 1). Otherwise, If all ranges have a `length` member or at least one is infinite,
this function is $(BIGOH min(r1.length, r2.length)). `_isSameLength`'s complexity is $(BIGOH 1). Otherwise, complexity is
$(BIGOH n), where `n` is the smallest of the lengths of ranges with unknown
length.
Infinite ranges are considered of the same length. An infinite range has never the same length as a Infinite ranges are considered of the same length. An infinite range has never
finite range. the same length as a finite range.
Params: Params:
r1 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives) rs = two or more $(REF_ALTTEXT input ranges, isInputRange, std,range,primitives)
r2 = a finite $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
Returns: Returns:
`true` if both ranges have the same length, `false` otherwise. `true` if both ranges have the same length, `false` otherwise.
*/ */
bool isSameLength(Range1, Range2)(Range1 r1, Range2 r2) bool isSameLength(Ranges...)(Ranges rs)
if (isInputRange!Range1 && isInputRange!Range2) if (allSatisfy!(isInputRange, Ranges))
{ {
static if (isInfinite!Range1 || isInfinite!Range2) static if (anySatisfy!(isInfinite, Ranges))
{ {
return isInfinite!Range1 && isInfinite!Range2; return allSatisfy!(isInfinite, Ranges);
} }
else static if (hasLength!(Range1) && hasLength!(Range2)) else static if (anySatisfy!(hasLength, Ranges))
{ {
return r1.length == r2.length; // Compute the O(1) length
auto baselineLength = size_t.max;
static foreach (i, R; Ranges)
{
static if (hasLength!R)
{
if (baselineLength == size_t.max)
baselineLength = rs[i].length;
else if (rs[i].length != baselineLength)
return false;
} }
else static if (hasLength!(Range1) && !hasLength!(Range2))
{
return r2.walkLength(r1.length + 1) == r1.length;
} }
else static if (!hasLength!(Range1) && hasLength!(Range2)) // Iterate all ranges without known length
foreach (_; 0 .. baselineLength)
static foreach (i, R; Ranges)
{ {
return r1.walkLength(r2.length + 1) == r2.length; static if (!hasLength!R)
{
// All must be non-empty
if (rs[i].empty)
return false;
rs[i].popFront;
}
}
static foreach (i, R; Ranges)
{
static if (!hasLength!R)
{
// All must be now empty
if (!rs[i].empty)
return false;
}
}
return true;
} }
else else
{ {
for (; !r1.empty; r1.popFront, r2.popFront) // All have unknown length, iterate in lockstep
for (;;)
static foreach (i, r; rs)
{
if (r.empty)
{
// One is empty, so all must be empty
static if (i != 0)
{ {
if (r2.empty)
return false; return false;
} }
return r2.empty; else
{
static foreach (j, r1; rs[1 .. $])
if (!r1.empty)
return false;
return true;
}
}
r.popFront;
}
} }
} }
@ -2139,6 +2180,13 @@ if (isInputRange!Range1 && isInputRange!Range2)
DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r11; DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Input) r11;
auto r12 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]); auto r12 = new ReferenceInputRange!int([1, 2, 3, 4, 5, 6, 7, 8]);
assert(!isSameLength(r11, r12)); assert(!isSameLength(r11, r12));
import std.algorithm.iteration : filter;
assert(isSameLength(filter!"a >= 1"([1, 2, 3]), [4, 5, 6]));
assert(!isSameLength(filter!"a > 1"([1, 2, 3]), [4, 5, 6]));
assert(isSameLength(filter!"a > 1"([1, 2, 3]), filter!"a > 4"([4, 5, 6])));
} }
// Still functional but not documented anymore. // Still functional but not documented anymore.