mirror of
https://github.com/dlang/phobos.git
synced 2025-05-02 08:00:48 +03:00
Changed chooseAmong implementation from recursive to linear.
This commit is contained in:
parent
bedcad87f8
commit
ab547c78c3
1 changed files with 141 additions and 101 deletions
|
@ -235,7 +235,7 @@ public import std.range.primitives;
|
|||
public import std.typecons : Flag, Yes, No;
|
||||
|
||||
import std.internal.attributes : betterC;
|
||||
import std.meta : allSatisfy, staticMap;
|
||||
import std.meta : allSatisfy, anySatisfy, staticMap;
|
||||
import std.traits : CommonType, isCallable, isFloatingPoint, isIntegral,
|
||||
isPointer, isSomeFunction, isStaticArray, Unqual, isInstanceOf;
|
||||
|
||||
|
@ -1412,7 +1412,8 @@ auto choose(R1, R2)(bool condition, return scope R1 r1, return scope R2 r2)
|
|||
if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) &&
|
||||
!is(CommonType!(ElementType!(Unqual!R1), ElementType!(Unqual!R2)) == void))
|
||||
{
|
||||
return ChooseResult!(R1, R2)(condition, r1, r2);
|
||||
size_t choice = condition? 0: 1;
|
||||
return ChooseResult!(R1, R2)(choice, r1, r2);
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -1447,76 +1448,102 @@ if (isInputRange!(Unqual!R1) && isInputRange!(Unqual!R2) &&
|
|||
}
|
||||
|
||||
|
||||
private struct ChooseResult(R1, R2)
|
||||
private struct ChooseResult(Ranges...)
|
||||
{
|
||||
import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor;
|
||||
import std.meta : aliasSeqOf, ApplyLeft;
|
||||
import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor,
|
||||
lvalueOf;
|
||||
|
||||
private union
|
||||
{
|
||||
R1 r1;
|
||||
R2 r2;
|
||||
Ranges rs;
|
||||
}
|
||||
private bool r1Chosen;
|
||||
private size_t chosenI;
|
||||
|
||||
private static auto ref actOnChosen(alias foo, ExtraArgs ...)(ref ChooseResult r,
|
||||
auto ref ExtraArgs extraArgs)
|
||||
private static auto ref actOnChosen(alias foo, ExtraArgs ...)
|
||||
(ref ChooseResult r, auto ref ExtraArgs extraArgs)
|
||||
{
|
||||
if (r.r1Chosen)
|
||||
ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; }
|
||||
|
||||
switch(r.chosenI)
|
||||
{
|
||||
ref get1(return ref ChooseResult r) @trusted { return r.r1; }
|
||||
return foo(get1(r), extraArgs);
|
||||
static foreach(candI; 0 .. rs.length)
|
||||
{
|
||||
case candI: return foo(getI!candI(r), extraArgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
ref get2(return ref ChooseResult r) @trusted { return r.r2; }
|
||||
return foo(get2(r), extraArgs);
|
||||
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
this(bool r1Chosen, return scope R1 r1, return scope R2 r2) @trusted
|
||||
// @trusted because of assignment of r which overlap each other
|
||||
this(size_t chosen, return scope Ranges rs) @trusted
|
||||
{
|
||||
// @trusted because of assignment of r1 and r2 which overlap each other
|
||||
import core.lifetime : emplace;
|
||||
|
||||
// This should be the only place r1Chosen is ever assigned
|
||||
// This should be the only place chosenI is ever assigned
|
||||
// independently
|
||||
this.r1Chosen = r1Chosen;
|
||||
if (r1Chosen)
|
||||
this.chosenI = chosen;
|
||||
|
||||
// Otherwise the compiler will complain about skipping these fields
|
||||
static foreach(i; 0 .. rs.length)
|
||||
{
|
||||
this.r2 = R2.init;
|
||||
emplace(&this.r1, r1);
|
||||
this.rs[i] = Ranges[i].init;
|
||||
}
|
||||
else
|
||||
|
||||
// The relevant field needs to be initialized last so it will overwrite
|
||||
// the other initializations and not the other way around.
|
||||
sw: switch(chosenI)
|
||||
{
|
||||
this.r1 = R1.init;
|
||||
emplace(&this.r2, r2);
|
||||
static foreach(i; 0 .. rs.length)
|
||||
{
|
||||
case i:
|
||||
this.rs[i] = rs[i];
|
||||
break sw;
|
||||
}
|
||||
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Some legacy code may still call this with typeof(choose(/*...*/))(/*...*/)
|
||||
// without this overload the regular constructor would invert the meaning of
|
||||
// the boolean
|
||||
static if(rs.length == 2)
|
||||
pragma(inline, true)
|
||||
deprecated("Call with size_t (0 = first), or use the choose function")
|
||||
this(bool firstChosen, Ranges rs)
|
||||
{
|
||||
import core.lifetime : move;
|
||||
this(cast(size_t)(firstChosen? 0: 1), rs[0].move, rs[1].move);
|
||||
}
|
||||
|
||||
void opAssign(ChooseResult r)
|
||||
{
|
||||
static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2)
|
||||
if (r1Chosen != r.r1Chosen)
|
||||
ref getI(size_t i)(return ref ChooseResult r) @trusted { return r.rs[i]; }
|
||||
|
||||
static if (anySatisfy!(hasElaborateDestructor, Ranges))
|
||||
if (chosenI != r.chosenI)
|
||||
{
|
||||
// destroy the current item
|
||||
actOnChosen!((ref r) => destroy(r))(this);
|
||||
}
|
||||
r1Chosen = r.r1Chosen;
|
||||
if (r1Chosen)
|
||||
chosenI = r.chosenI;
|
||||
|
||||
sw: switch(chosenI)
|
||||
{
|
||||
ref get1(return ref ChooseResult r) @trusted { return r.r1; }
|
||||
get1(this) = get1(r);
|
||||
static foreach(candI; 0 .. rs.length)
|
||||
{
|
||||
case candI: getI!candI(this) = getI!candI(r);
|
||||
break sw;
|
||||
}
|
||||
else
|
||||
{
|
||||
ref get2(return ref ChooseResult r) @trusted { return r.r2; }
|
||||
get2(this) = get2(r);
|
||||
|
||||
default: assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Carefully defined postblit to postblit the appropriate range
|
||||
static if (hasElaborateCopyConstructor!R1
|
||||
|| hasElaborateCopyConstructor!R2)
|
||||
static if (anySatisfy!(hasElaborateCopyConstructor, Ranges))
|
||||
this(this)
|
||||
{
|
||||
actOnChosen!((ref r) {
|
||||
|
@ -1524,17 +1551,15 @@ private struct ChooseResult(R1, R2)
|
|||
})(this);
|
||||
}
|
||||
|
||||
static if (hasElaborateDestructor!R1 || hasElaborateDestructor!R2)
|
||||
static if (anySatisfy!(hasElaborateDestructor, Ranges))
|
||||
~this()
|
||||
{
|
||||
actOnChosen!((ref r) => destroy(r))(this);
|
||||
}
|
||||
|
||||
static if (isInfinite!R1 && isInfinite!R2)
|
||||
// Propagate infiniteness.
|
||||
enum bool empty = false;
|
||||
else
|
||||
@property bool empty()
|
||||
static if (allSatisfy!(isInfinite, Ranges)) enum bool empty = false;
|
||||
else @property bool empty()
|
||||
{
|
||||
return actOnChosen!(r => r.empty)(this);
|
||||
}
|
||||
|
@ -1550,34 +1575,38 @@ private struct ChooseResult(R1, R2)
|
|||
return actOnChosen!((ref r) { r.popFront; })(this);
|
||||
}
|
||||
|
||||
static if (isForwardRange!R1 && isForwardRange!R2)
|
||||
@property auto save() return scope
|
||||
static if (allSatisfy!(isForwardRange, Ranges))
|
||||
@property auto save() // return scope inferred
|
||||
{
|
||||
if (r1Chosen)
|
||||
auto saveOrInit(size_t i)()
|
||||
{
|
||||
ref R1 getR1() @trusted { return r1; }
|
||||
return ChooseResult(r1Chosen, getR1.save, R2.init);
|
||||
}
|
||||
else
|
||||
{
|
||||
ref R2 getR2() @trusted { return r2; }
|
||||
return ChooseResult(r1Chosen, R1.init, getR2.save);
|
||||
}
|
||||
ref getI() @trusted { return rs[i]; }
|
||||
if (i == chosenI) return getI().save;
|
||||
else return Ranges[i].init;
|
||||
}
|
||||
|
||||
@property void front(T)(T v)
|
||||
if (is(typeof({ r1.front = v; r2.front = v; })))
|
||||
return typeof(this)(chosenI, staticMap!(saveOrInit,
|
||||
aliasSeqOf!(rs.length.iota)));
|
||||
}
|
||||
|
||||
template front(T)
|
||||
{
|
||||
private enum overloadValidFor(alias r) = is(typeof(r.front = T.init));
|
||||
|
||||
static if (allSatisfy!(overloadValidFor, rs))
|
||||
void front(T v)
|
||||
{
|
||||
actOnChosen!((ref r, T v) { r.front = v; })(this, v);
|
||||
}
|
||||
}
|
||||
|
||||
static if (hasMobileElements!R1 && hasMobileElements!R2)
|
||||
static if (allSatisfy!(hasMobileElements, Ranges))
|
||||
auto moveFront()
|
||||
{
|
||||
return actOnChosen!((ref r) => r.moveFront)(this);
|
||||
}
|
||||
|
||||
static if (isBidirectionalRange!R1 && isBidirectionalRange!R2)
|
||||
static if (allSatisfy!(isBidirectionalRange, Ranges))
|
||||
{
|
||||
@property auto ref back()
|
||||
{
|
||||
|
@ -1590,20 +1619,25 @@ private struct ChooseResult(R1, R2)
|
|||
actOnChosen!((ref r) { r.popBack; })(this);
|
||||
}
|
||||
|
||||
static if (hasMobileElements!R1 && hasMobileElements!R2)
|
||||
static if (allSatisfy!(hasMobileElements, Ranges))
|
||||
auto moveBack()
|
||||
{
|
||||
return actOnChosen!((ref r) => r.moveBack)(this);
|
||||
}
|
||||
|
||||
@property void back(T)(T v)
|
||||
if (is(typeof({ r1.back = v; r2.back = v; })))
|
||||
template back(T)
|
||||
{
|
||||
private enum overloadValidFor(alias r) = is(typeof(r.back = T.init));
|
||||
|
||||
static if (allSatisfy!(overloadValidFor, rs))
|
||||
void back(T v)
|
||||
{
|
||||
actOnChosen!((ref r, T v) { r.back = v; })(this, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static if (hasLength!R1 && hasLength!R2)
|
||||
static if (allSatisfy!(hasLength, Ranges))
|
||||
{
|
||||
@property size_t length()
|
||||
{
|
||||
|
@ -1612,7 +1646,7 @@ private struct ChooseResult(R1, R2)
|
|||
alias opDollar = length;
|
||||
}
|
||||
|
||||
static if (isRandomAccessRange!R1 && isRandomAccessRange!R2)
|
||||
static if (allSatisfy!(isRandomAccessRange, Ranges))
|
||||
{
|
||||
auto ref opIndex(size_t index)
|
||||
{
|
||||
|
@ -1620,32 +1654,40 @@ private struct ChooseResult(R1, R2)
|
|||
return actOnChosen!get(this, index);
|
||||
}
|
||||
|
||||
static if (hasMobileElements!R1 && hasMobileElements!R2)
|
||||
static if (allSatisfy!(hasMobileElements, Ranges))
|
||||
auto moveAt(size_t index)
|
||||
{
|
||||
return actOnChosen!((ref r, size_t index) => r.moveAt(index))
|
||||
(this, index);
|
||||
}
|
||||
|
||||
void opIndexAssign(T)(T v, size_t index)
|
||||
if (is(typeof({ r1[1] = v; r2[1] = v; })))
|
||||
private enum indexAssignable(T, R) = is(typeof(lvalueOf!R[1] = T.init));
|
||||
|
||||
template opIndexAssign(T)
|
||||
if (allSatisfy!(ApplyLeft!(indexAssignable, T), Ranges))
|
||||
{
|
||||
void opIndexAssign(T v, size_t index)
|
||||
{
|
||||
return actOnChosen!((ref r, size_t index, T v) { r[index] = v; })
|
||||
(this, index, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static if (hasSlicing!R1 && hasSlicing!R2)
|
||||
static if (allSatisfy!(hasSlicing, Ranges))
|
||||
auto opSlice(size_t begin, size_t end)
|
||||
{
|
||||
alias Slice1 = typeof(R1.init[0 .. 1]);
|
||||
alias Slice2 = typeof(R2.init[0 .. 1]);
|
||||
return actOnChosen!((r, size_t begin, size_t end) {
|
||||
static if (is(typeof(r) == Slice1))
|
||||
return choose(true, r[begin .. end], Slice2.init);
|
||||
else
|
||||
return choose(false, Slice1.init, r[begin .. end]);
|
||||
})(this, begin, end);
|
||||
alias Slice(R) = typeof(R.init[0 .. 1]);
|
||||
alias Slices = staticMap!(Slice, Ranges);
|
||||
|
||||
auto sliceOrInit(size_t i)()
|
||||
{
|
||||
ref getI() @trusted { return rs[i]; }
|
||||
return i == chosenI? getI()[begin .. end]: Slices[i].init;
|
||||
}
|
||||
|
||||
return chooseAmong(chosenI, staticMap!(sliceOrInit,
|
||||
aliasSeqOf!(rs.length.iota)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1668,8 +1710,9 @@ pure @safe unittest
|
|||
int front;
|
||||
bool empty;
|
||||
void popFront() {}
|
||||
@property R save() { p = q; return this; }
|
||||
// `p = q;` is only there to prevent inference of `scope return`.
|
||||
@property @safe R save() { p = q; return this; }
|
||||
|
||||
}
|
||||
R r;
|
||||
choose(true, r, r).save;
|
||||
|
@ -1801,10 +1844,7 @@ if (Ranges.length >= 2
|
|||
&& allSatisfy!(isInputRange, staticMap!(Unqual, Ranges))
|
||||
&& !is(CommonType!(staticMap!(ElementType, Ranges)) == void))
|
||||
{
|
||||
static if (Ranges.length == 2)
|
||||
return choose(index == 0, rs[0], rs[1]);
|
||||
else
|
||||
return choose(index == 0, rs[0], chooseAmong(index - 1, rs[1 .. $]));
|
||||
return ChooseResult!Ranges(index, rs);
|
||||
}
|
||||
|
||||
///
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue