diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index f9d0a004c..e70f49078 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -2404,20 +2404,49 @@ Params: needle = What to look for. Returns: - A tuple of the split portions of `haystack` (see above for details). + +A sub-type of Tuple!() of the split portions of `haystack` (see above for +details). This sub-type of Tuple!() has opCast defined for bool. This opCast +returns $(D true) when the separating $(D needle) was found (!result[1].empty) +and $(D false) otherwise. This enables the convenient idiom shown in the +following example. + +Example: +--- +if (const split = haystack.findSplit(needle)) +{ + doSomethingWithSplit(split); +} +--- */ auto findSplit(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if (isForwardRange!R1 && isForwardRange!R2) { + static struct Result(S1, S2) if (isForwardRange!S1 && + isForwardRange!S2) + { + this(S1 pre, S1 separator, S2 post) + { + asTuple = typeof(asTuple)(pre, separator, post); + } + Tuple!(S1, S1, S2) asTuple; + bool opCast(T : bool)() + { + return !asTuple[1].empty; + } + alias asTuple this; + } + static if (isSomeString!R1 && isSomeString!R2 || isRandomAccessRange!R1 && hasLength!R2) { auto balance = find!pred(haystack, needle); immutable pos1 = haystack.length - balance.length; immutable pos2 = balance.empty ? pos1 : pos1 + needle.length; - return tuple(haystack[0 .. pos1], - haystack[pos1 .. pos2], - haystack[pos2 .. haystack.length]); + return Result!(typeof(haystack[0 .. pos1]), + typeof(haystack[pos2 .. haystack.length]))(haystack[0 .. pos1], + haystack[pos1 .. pos2], + haystack[pos2 .. haystack.length]); } else { @@ -2442,9 +2471,10 @@ if (isForwardRange!R1 && isForwardRange!R2) pos2 = ++pos1; } } - return tuple(takeExactly(original, pos1), - takeExactly(haystack, pos2 - pos1), - h); + return Result!(typeof(takeExactly(original, pos1)), + typeof(h))(takeExactly(original, pos1), + takeExactly(haystack, pos2 - pos1), + h); } } @@ -2452,12 +2482,29 @@ if (isForwardRange!R1 && isForwardRange!R2) auto findSplitBefore(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if (isForwardRange!R1 && isForwardRange!R2) { + static struct Result(S1, S2) if (isForwardRange!S1 && + isForwardRange!S2) + { + this(S1 pre, S2 post) + { + asTuple = typeof(asTuple)(pre, post); + } + Tuple!(S1, S2) asTuple; + bool opCast(T : bool)() + { + return !asTuple[0].empty; + } + alias asTuple this; + } + static if (isSomeString!R1 && isSomeString!R2 || isRandomAccessRange!R1 && hasLength!R2) { auto balance = find!pred(haystack, needle); immutable pos = haystack.length - balance.length; - return tuple(haystack[0 .. pos], haystack[pos .. haystack.length]); + return Result!(typeof(haystack[0 .. pos]), + typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos], + haystack[pos .. haystack.length]); } else { @@ -2481,7 +2528,9 @@ if (isForwardRange!R1 && isForwardRange!R2) ++pos; } } - return tuple(takeExactly(original, pos), haystack); + return Result!(typeof(takeExactly(original, pos)), + typeof(haystack))(takeExactly(original, pos), + haystack); } } @@ -2489,12 +2538,29 @@ if (isForwardRange!R1 && isForwardRange!R2) auto findSplitAfter(alias pred = "a == b", R1, R2)(R1 haystack, R2 needle) if (isForwardRange!R1 && isForwardRange!R2) { + static struct Result(S1, S2) if (isForwardRange!S1 && + isForwardRange!S2) + { + this(S1 pre, S2 post) + { + asTuple = typeof(asTuple)(pre, post); + } + Tuple!(S1, S2) asTuple; + bool opCast(T : bool)() + { + return !asTuple[1].empty; + } + alias asTuple this; + } + static if (isSomeString!R1 && isSomeString!R2 || isRandomAccessRange!R1 && hasLength!R2) { auto balance = find!pred(haystack, needle); immutable pos = balance.empty ? 0 : haystack.length - balance.length + needle.length; - return tuple(haystack[0 .. pos], haystack[pos .. haystack.length]); + return Result!(typeof(haystack[0 .. pos]), + typeof(haystack[pos .. haystack.length]))(haystack[0 .. pos], + haystack[pos .. haystack.length]); } else { @@ -2508,7 +2574,9 @@ if (isForwardRange!R1 && isForwardRange!R2) if (h.empty) { // Failed search - return tuple(takeExactly(original, 0), original); + return Result!(typeof(takeExactly(original, 0)), + typeof(original))(takeExactly(original, 0), + original); } if (binaryFun!pred(h.front, n.front)) { @@ -2524,7 +2592,9 @@ if (isForwardRange!R1 && isForwardRange!R2) pos2 = ++pos1; } } - return tuple(takeExactly(original, pos2), h); + return Result!(typeof(takeExactly(original, pos2)), + typeof(h))(takeExactly(original, pos2), + h); } } @@ -2533,6 +2603,10 @@ if (isForwardRange!R1 && isForwardRange!R2) { auto a = "Carl Sagan Memorial Station"; auto r = findSplit(a, "Velikovsky"); + import std.typecons : isTuple; + static assert(isTuple!(typeof(r.asTuple))); + static assert(isTuple!(typeof(r))); + assert(!r); assert(r[0] == a); assert(r[1].empty); assert(r[2].empty); @@ -2541,9 +2615,11 @@ if (isForwardRange!R1 && isForwardRange!R2) assert(r[1] == " "); assert(r[2] == "Sagan Memorial Station"); auto r1 = findSplitBefore(a, "Sagan"); + assert(r1); assert(r1[0] == "Carl ", r1[0]); assert(r1[1] == "Sagan Memorial Station"); auto r2 = findSplitAfter(a, "Sagan"); + assert(r2); assert(r2[0] == "Carl Sagan"); assert(r2[1] == " Memorial Station"); } @@ -2552,27 +2628,33 @@ if (isForwardRange!R1 && isForwardRange!R2) { auto a = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; auto r = findSplit(a, [9, 1]); + assert(!r); assert(r[0] == a); assert(r[1].empty); assert(r[2].empty); r = findSplit(a, [3]); + assert(r); assert(r[0] == a[0 .. 2]); assert(r[1] == a[2 .. 3]); assert(r[2] == a[3 .. $]); auto r1 = findSplitBefore(a, [9, 1]); + assert(r1); assert(r1[0] == a); assert(r1[1].empty); r1 = findSplitBefore(a, [3, 4]); + assert(r1); assert(r1[0] == a[0 .. 2]); assert(r1[1] == a[2 .. $]); - r1 = findSplitAfter(a, [9, 1]); - assert(r1[0].empty); - assert(r1[1] == a); - r1 = findSplitAfter(a, [3, 4]); - assert(r1[0] == a[0 .. 4]); - assert(r1[1] == a[4 .. $]); + auto r2 = findSplitAfter(a, [9, 1]); + assert(r2); + assert(r2[0].empty); + assert(r2[1] == a); + r2 = findSplitAfter(a, [3, 4]); + assert(r2); + assert(r2[0] == a[0 .. 4]); + assert(r2[1] == a[4 .. $]); } @safe unittest @@ -2583,27 +2665,33 @@ if (isForwardRange!R1 && isForwardRange!R2) auto a = [ 1, 2, 3, 4, 5, 6, 7, 8 ]; auto fwd = filter!"a > 0"(a); auto r = findSplit(fwd, [9, 1]); + assert(!r); assert(equal(r[0], a)); assert(r[1].empty); assert(r[2].empty); r = findSplit(fwd, [3]); + assert(r); assert(equal(r[0], a[0 .. 2])); assert(equal(r[1], a[2 .. 3])); assert(equal(r[2], a[3 .. $])); auto r1 = findSplitBefore(fwd, [9, 1]); + assert(r1); assert(equal(r1[0], a)); assert(r1[1].empty); r1 = findSplitBefore(fwd, [3, 4]); + assert(r1); assert(equal(r1[0], a[0 .. 2])); assert(equal(r1[1], a[2 .. $])); - r1 = findSplitAfter(fwd, [9, 1]); - assert(r1[0].empty); - assert(equal(r1[1], a)); - r1 = findSplitAfter(fwd, [3, 4]); - assert(equal(r1[0], a[0 .. 4])); - assert(equal(r1[1], a[4 .. $])); + auto r2 = findSplitAfter(fwd, [9, 1]); + assert(r2); + assert(r2[0].empty); + assert(equal(r2[1], a)); + r2 = findSplitAfter(fwd, [3, 4]); + assert(r2); + assert(equal(r2[0], a[0 .. 4])); + assert(equal(r2[1], a[4 .. $])); } /** @@ -3528,4 +3616,3 @@ unittest // Issue 13124 auto s = "hello how\nare you"; s.until!(c => c.among!('\n', '\r')); } - diff --git a/std/typecons.d b/std/typecons.d index e33854384..300862826 100644 --- a/std/typecons.d +++ b/std/typecons.d @@ -1376,17 +1376,11 @@ unittest Returns: true if `T` is a `Tuple` type, false otherwise. */ -template isTuple(T) -{ - static if (is(Unqual!T Unused : Tuple!Specs, Specs...)) - { - enum isTuple = true; - } - else - { - enum isTuple = false; - } -} +enum isTuple(T) = __traits(compiles, + { + void f(Specs...)(Tuple!Specs tup) {} + f(T.init); + } ); /// unittest @@ -6879,4 +6873,3 @@ unittest string[3] function(string[] arr, string[2] ...) pure @trusted, ); } -