diff --git a/std/algorithm/iteration.d b/std/algorithm/iteration.d index 95620a63dbe..b9af5c82956 100644 --- a/std/algorithm/iteration.d +++ b/std/algorithm/iteration.d @@ -457,35 +457,81 @@ template map(fun...) if (fun.length >= 1) { auto map(Range)(Range r) if (isInputRange!(Unqual!Range)) { - import std.meta : AliasSeq, staticMap; - - alias RE = ElementType!(Range); - static if (fun.length > 1) + import std.range; + import std.experimental.ndslice.slice : Slice; + // Type_normalization: ndslice.map -> ndslice + static if (is(Range : Slice!(N, R), size_t N, R)) + { + import std.experimental.ndslice.selection: pack, mapSlice; + static if (N == 1) + return r.mapSlice!fun; + else + return r.pack!(N - 1).mapSlice!fun; + } + else + // Type_normalization: take.map -> map.take + static if (is(Range : Take!R, R)) + { + return .map!fun(r.source).take(r._maxAvailable); + } + else + // Type_normalization: indexed.map -> map.indexed + static if (is(Range : Indexed!(R, I), R, I)) + { + return .map!fun(r.source).indexed(r.indices); + } + else + // Type_normalization: strided.map -> map.strided + static if (is(typeof(stride(r.source, size_t(1))) == Range)) + { + return .map!fun(r.source).strided(r.stride); + } + else + // Type_normalization: retro.map -> map.retro + static if (is(typeof(retro(r.source)) == Range)) + { + return .map!fun(r.source).retro; + } + else { - import std.functional : adjoin; - import std.meta : staticIndexOf; + import std.meta : AliasSeq, staticMap; - alias _funs = staticMap!(unaryFun, fun); - alias _fun = adjoin!_funs; + alias RE = ElementType!(Range); + static if (fun.length > 1) + { + import std.functional : adjoin; + import std.meta : staticIndexOf; + + alias _funs = staticMap!(unaryFun, fun); + alias _fun = adjoin!_funs; - // Once DMD issue #5710 is fixed, this validation loop can be moved into a template. - foreach (f; _funs) + // Once DMD issue #5710 is fixed, this validation loop can be moved into a template. + foreach (f; _funs) + { + static assert(!is(typeof(f(RE.init)) == void), + "Mapping function(s) must not return void: " ~ _funs.stringof); + } + } + else { - static assert(!is(typeof(f(RE.init)) == void), + alias _fun = unaryFun!fun; + alias _funs = AliasSeq!(_fun); + + // Do the validation separately for single parameters due to DMD issue #15777. + static assert(!is(typeof(_fun(RE.init)) == void), "Mapping function(s) must not return void: " ~ _funs.stringof); } + // Type_normalization: map!a.map!b -> map!(b(a)) + static if (is(Range : MapResult!(F, R), F, R)) + { + import std.functional : compose; + return MapResult!(compose(_fun, F), R)(r._input); + } + else + { + return MapResult!(_fun, Range)(r); + } } - else - { - alias _fun = unaryFun!fun; - alias _funs = AliasSeq!(_fun); - - // Do the validation separately for single parameters due to DMD issue #15777. - static assert(!is(typeof(_fun(RE.init)) == void), - "Mapping function(s) must not return void: " ~ _funs.stringof); - } - - return MapResult!(_fun, Range)(r); } } diff --git a/std/experimental/ndslice/slice.d b/std/experimental/ndslice/slice.d index 2b6957c3706..954975d1987 100644 --- a/std/experimental/ndslice/slice.d +++ b/std/experimental/ndslice/slice.d @@ -360,6 +360,7 @@ pure nothrow unittest assert(f == iotaSlice(5, 3)); } +version(none) // FIXME: unittest cycle dependency nothrow unittest { import std.experimental.ndslice.selection : iotaSlice; @@ -693,6 +694,7 @@ makeSlice(T, } /// +version(none) // FIXME: unittest cycle dependency @nogc unittest { import std.experimental.allocator; @@ -714,6 +716,7 @@ makeSlice(T, } /// Initialization with default value +version(none) // FIXME: unittest cycle dependency @nogc unittest { import std.experimental.allocator; @@ -725,6 +728,7 @@ makeSlice(T, Mallocator.instance.dispose(tup.array); } +version(none) // FIXME: unittest cycle dependency @nogc unittest { import std.experimental.allocator; @@ -761,6 +765,7 @@ makeUninitializedSlice(T, } /// +version(none) // FIXME: unittest cycle dependency @nogc unittest { import std.experimental.allocator; @@ -843,6 +848,7 @@ auto makeNdarray(T, Allocator, size_t N, Range)(auto ref Allocator alloc, Slice } /// +version(none) // FIXME: unittest cycle dependency @nogc unittest { import std.experimental.allocator; diff --git a/std/range/package.d b/std/range/package.d index 730db8bc69f..df189587892 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -1,5 +1,3 @@ -// Written in the D programming language. - /** This module defines the notion of a range. Ranges generalize the concept of arrays, lists, or anything that involves sequential access. This abstraction @@ -183,8 +181,10 @@ to $(HTTP fantascienza.net/leonardo/so/, Leonardo Maffi). */ module std.range; -public import std.range.primitives; +deprecated("use explicit `import std.range.interfaces;`") public import std.range.interfaces; + +public import std.range.primitives; public import std.array; public import std.typecons : Flag, Yes, No; @@ -209,14 +209,39 @@ Returns: auto retro(Range)(Range r) if (isBidirectionalRange!(Unqual!Range)) { - // Check for retro(retro(r)) and just return r in that case + import std.experimental.ndslice.slice : Slice; + + // Type_normalization: ndslice.retro -> ndslice + static if (is(Range : Slice!(N, R), size_t N, R)) + { + import std.experimental.ndslice.iteration : reversed; + return r.reversed!0; + } + else + // Type_normalization: .indexed(indexes).retro -> .indexed(indexes.retro) + static if (is(Range : Indexed!(R, I), R, I)) + { + return r.source.indexed(r.indices.retro); + } + else + // Type_normalization: .stride(n).retro -> .retro.stride(n) + static if (is(typeof(stride(r.source, size_t(1))) == Range)) + { + /// strided biderectional range must have a length + static assert(hasLength!(typeof(r.source)), "Phobos internal error: please file a bug."); + if (!r.source.empty) + r.source.popBackExactly((r.source.length - 1) % r._n); + return r.source.retro.stride(r._n); + } + else + // Type_normalization: .retro.retro -> source static if (is(typeof(retro(r.source)) == Range)) { return r.source; } else { - static struct Result() + static struct Result { private alias R = Unqual!Range; @@ -260,16 +285,16 @@ if (isBidirectionalRange!(Unqual!Range)) } } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) { - @property void front(ElementType!R val) + @property auto front(ElementType!R val) { - source.back = val; + return source.back = val; } - @property void back(ElementType!R val) + @property auto back(ElementType!R val) { - source.front = val; + return source.front = val; } } @@ -277,11 +302,11 @@ if (isBidirectionalRange!(Unqual!Range)) { auto ref opIndex(size_t n) { return source[retroIndex(n)]; } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) { - void opIndexAssign(ElementType!R val, size_t n) + auto opIndexAssign(ElementType!R val, size_t n) { - source[retroIndex(n)] = val; + return source[retroIndex(n)] = val; } } @@ -311,7 +336,7 @@ if (isBidirectionalRange!(Unqual!Range)) } } - return Result!()(r); + return Result(r); } } @@ -455,15 +480,30 @@ in } body { - import std.algorithm.comparison : min; + import std.experimental.ndslice.slice : Slice; + // Type_normalization: ndslice.stride -> ndslice + static if (is(Range : Slice!(N, R), size_t N, R)) + { + import std.experimental.ndslice.iteration : strided; + return r.strided!0(n); + } + else + // Type_normalization: .indexed(indexes).stride -> .indexed(indexes.stride) + static if (is(Range : Indexed!(R, I), R, I)) + { + return r.source.indexed(r.indices.stride(n)); + } + else + // Type_normalization: stride.stride -> stride static if (is(typeof(stride(r.source, n)) == Range)) { - // stride(stride(r, n1), n2) is stride(r, n1 * n2) return stride(r.source, r._n * n); } else { + import std.algorithm.comparison : min; + static struct Result { private alias R = Unqual!Range; @@ -504,6 +544,11 @@ body } } + size_t stride() @property + { + return _n; + } + static if (isForwardRange!R) { @property auto save() @@ -537,11 +582,11 @@ body } } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) { - @property void front(ElementType!R val) + @property auto front(ElementType!R val) { - source.front = val; + return source.front = val; } } @@ -572,17 +617,17 @@ body } } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) { - @property void back(ElementType!R val) + @property auto back(ElementType!R val) { eliminateSlackElements(); - source.back = val; + return source.back = val; } } } - static if (isRandomAccessRange!R && hasLength!R) + static if (isRandomAccessRange!R) { auto ref opIndex(size_t n) { @@ -600,11 +645,11 @@ body } } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) { - void opIndexAssign(ElementType!R val, size_t n) + auto opIndexAssign(ElementType!R val, size_t n) { - source[_n * n] = val; + return source[_n * n] = val; } } } @@ -936,7 +981,7 @@ if (Ranges.length > 0 && assert(false); } - static if (allSameType && allSatisfy!(hasAssignableElements, R)) + static if (allSameType && allSatisfy!(hasAssignableElements, R) && !allSatisfy!(hasLvalueElements, R)) { // @@@BUG@@@ //@property void front(T)(T v) if (is(T : RvalueElementType)) @@ -1001,7 +1046,7 @@ if (Ranges.length > 0 && } } - static if (allSameType && allSatisfy!(hasAssignableElements, R)) + static if (allSameType && allSatisfy!(hasAssignableElements, R) && !allSatisfy!(hasLvalueElements, R)) { @property void back(RvalueElementType v) { @@ -1072,7 +1117,7 @@ if (Ranges.length > 0 && } } - static if (allSameType && allSatisfy!(hasAssignableElements, R)) + static if (allSameType && allSatisfy!(hasAssignableElements, R) && !allSatisfy!(hasLvalueElements, R)) void opIndexAssign(ElementType v, size_t index) { foreach (i, Range; R) @@ -1873,7 +1918,8 @@ if (isInputRange!(Unqual!Range) && /// User accessible in read and write public R source; - private size_t _maxAvailable; + // used in std.algorithm.iteration : map + package(std) size_t _maxAvailable; alias Source = R; @@ -1909,15 +1955,15 @@ if (isInputRange!(Unqual!Range) && return Take(source.save, _maxAvailable); } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) /// ditto - @property void front(ElementType!R v) + @property auto front(ElementType!R v) { assert(!empty, "Attempting to assign to the front of an empty " ~ Take.stringof); // This has to return auto instead of void because of Bug 4706. - source.front = v; + return source.front = v; } static if (hasMobileElements!R) @@ -1996,25 +2042,25 @@ if (isInputRange!(Unqual!Range) && return source[index]; } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) { /// ditto - @property void back(ElementType!R v) + @property auto back(ElementType!R v) { // This has to return auto instead of void because of Bug 4706. assert(!empty, "Attempting to assign to the back of an empty " ~ Take.stringof); - source[this.length - 1] = v; + return source[this.length - 1] = v; } /// ditto - void opIndexAssign(ElementType!R v, size_t index) + auto opIndexAssign(ElementType!R v, size_t index) { assert(index < length, "Attempting to index out of the bounds of a " ~ Take.stringof); - source[index] = v; + return source[index] = v; } } @@ -2307,9 +2353,9 @@ if (isInputRange!R) } } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) { - @property auto ref front(ElementType!R v) + @property auto front(ElementType!R v) { assert(!empty, "Attempting to assign to the front of an empty " @@ -3361,12 +3407,12 @@ struct Cycle(R) } } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) { /// ditto - @property void front(ElementType!R val) + @property auto front(ElementType!R val) { - _original[_index] = val; + return _original[_index] = val; } } @@ -3397,12 +3443,12 @@ struct Cycle(R) } } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) { /// ditto - void opIndexAssign(ElementType!R val, size_t n) + auto opIndexAssign(ElementType!R val, size_t n) { - _original[(n + _index) % _original.length] = val; + return _original[(n + _index) % _original.length] = val; } } @@ -3465,7 +3511,7 @@ struct Cycle(R) } } - static if (hasAssignableElements!R) + static if (hasAssignableElements!R && !hasLvalueElements!R) { /// ditto @property auto front(ElementType!R val) @@ -5318,8 +5364,8 @@ if (is(typeof(iota(E(0), end)))) /// Ditto // Specialization for floating-point types -auto iota(B, E, S)(B begin, E end, S step) -if (isFloatingPoint!(CommonType!(B, E, S))) +auto iota(T)(const T begin, const T end, const T step) + if (isFloatingPoint!T) in { assert(step != 0, "iota: step must not be 0"); @@ -5327,13 +5373,12 @@ in } body { - alias Value = Unqual!(CommonType!(B, E, S)); static struct Result { - private Value start, step; + private T start, step; private size_t index, count; - this(Value start, Value end, Value step) + this(T start, T end, T step) { import std.conv : to; @@ -5355,13 +5400,13 @@ body } @property bool empty() const { return index == count; } - @property Value front() const { assert(!empty); return start + step * index; } + @property T front() const { assert(!empty); return start + step * index; } void popFront() { assert(!empty); ++index; } - @property Value back() const + @property T back() const { assert(!empty); return start + step * (count - 1); @@ -5374,7 +5419,7 @@ body @property auto save() { return this; } - Value opIndex(size_t n) const + T opIndex(size_t n) const { assert(n < count); return start + step * (n + index); @@ -5809,11 +5854,11 @@ struct FrontTransversal(Ror, } } - static if (hasAssignableElements!RangeType) + static if (hasAssignableElements!RangeType && !hasLvalueElements!RangeType) { - @property void front(ElementType val) + @property auto front(ElementType val) { - _input.front.front = val; + return _input.front.front = val; } } @@ -5866,11 +5911,11 @@ struct FrontTransversal(Ror, } } - static if (hasAssignableElements!RangeType) + static if (hasAssignableElements!RangeType && !hasLvalueElements!RangeType) { - @property void back(ElementType val) + @property auto back(ElementType val) { - _input.back.front = val; + return _input.back.front = val; } } } @@ -5899,11 +5944,11 @@ struct FrontTransversal(Ror, } } /// Ditto - static if (hasAssignableElements!RangeType) + static if (hasAssignableElements!RangeType && !hasLvalueElements!RangeType) { - void opIndexAssign(ElementType val, size_t n) + auto opIndexAssign(ElementType val, size_t n) { - _input[n].front = val; + return _input[n].front = val; } } /// Ditto @@ -5937,12 +5982,32 @@ private: } /// Ditto -FrontTransversal!(RangeOfRanges, opt) frontTransversal( +auto frontTransversal( TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges) (RangeOfRanges rr) { - return typeof(return)(rr); + import std.experimental.ndslice.slice : Slice; + static if (is(RangeOfRanges : Slice!(N, R), size_t N, R)) + { + import std.experimental.ndslice.iteration : transposed; + rr = transposed!(1, 0)(rr); + static if (opt == TransverseOptions.assumeJagged) + { + if (rr.length) + return rr.front; + else + return typeof(rr.front).init; + } + else + { + return rr.front; + } + } + else + { + return FrontTransversal!(RangeOfRanges, opt)(rr); + } } /// @@ -6125,11 +6190,11 @@ struct Transversal(Ror, } /// Ditto - static if (hasAssignableElements!InnerRange) + static if (hasAssignableElements!InnerRange && !hasLvalueElements!InnerRange) { - @property void front(E val) + @property auto front(E val) { - _input.front[_n] = val; + return _input.front[_n] = val; } } @@ -6183,11 +6248,11 @@ struct Transversal(Ror, } /// Ditto - static if (hasAssignableElements!InnerRange) + static if (hasAssignableElements!InnerRange && !hasLvalueElements!InnerRange) { - @property void back(E val) + @property auto back(E val) { - _input.back[_n] = val; + return _input.back[_n] = val; } } @@ -6218,11 +6283,11 @@ struct Transversal(Ror, } /// Ditto - static if (hasAssignableElements!InnerRange) + static if (hasAssignableElements!InnerRange && !hasLvalueElements!InnerRange) { - void opIndexAssign(E val, size_t n) + auto opIndexAssign(E val, size_t n) { - _input[n][_n] = val; + return _input[n][_n] = val; } } @@ -6258,11 +6323,31 @@ private: } /// Ditto -Transversal!(RangeOfRanges, opt) transversal +auto transversal (TransverseOptions opt = TransverseOptions.assumeJagged, RangeOfRanges) (RangeOfRanges rr, size_t n) { - return typeof(return)(rr, n); + import std.experimental.ndslice.slice : Slice; + static if (is(RangeOfRanges : Slice!(N, R), size_t N, R)) + { + import std.experimental.ndslice.iteration : transposed; + rr = transposed!(1, 0)(rr); + static if (opt == TransverseOptions.assumeJagged) + { + if (n < rr.length) + return rr[n]; + else + return typeof(rr[n]).init; + } + else + { + return rr[n]; + } + } + else + { + return Transversal!(RangeOfRanges, opt)(rr, n); + } } /// @@ -6422,12 +6507,21 @@ unittest Given a range of ranges, returns a range of ranges where the $(I i)'th subrange contains the $(I i)'th elements of the original subranges. */ -Transposed!RangeOfRanges transposed(RangeOfRanges)(RangeOfRanges rr) +auto transposed(RangeOfRanges)(RangeOfRanges rr) if (isForwardRange!RangeOfRanges && isInputRange!(ElementType!RangeOfRanges) && hasAssignableElements!RangeOfRanges) { - return Transposed!RangeOfRanges(rr); + import std.experimental.ndslice.slice : Slice; + static if (is(RangeOfRanges : Slice!(N, R), size_t N, R)) + { + import std.experimental.ndslice.iteration : transposed; + return transposed!(1, 0)(rr); + } + else + { + return Transposed!RangeOfRanges(rr); + } } /// @@ -6534,9 +6628,9 @@ struct Indexed(Source, Indices) } /// Ditto - static if (hasAssignableElements!Source) + static if (hasAssignableElements!Source && !hasLvalueElements!Source) { - @property auto ref front(ElementType!Source newVal) + @property auto front(ElementType!Source newVal) { assert(!empty); return _source[_indices.front] = newVal; @@ -6571,9 +6665,9 @@ struct Indexed(Source, Indices) } /// Ditto - static if (hasAssignableElements!Source) + static if (hasAssignableElements!Source && !hasLvalueElements!Source) { - @property auto ref back(ElementType!Source newVal) + @property auto back(ElementType!Source newVal) { assert(!empty); return _source[_indices.back] = newVal; @@ -6621,7 +6715,7 @@ struct Indexed(Source, Indices) } - static if (hasAssignableElements!Source) + static if (hasAssignableElements!Source && !hasLvalueElements!Source) { /// Ditto auto opIndexAssign(ElementType!Source newVal, size_t index) @@ -8877,17 +8971,19 @@ public: } else { - @property auto front() + @property auto ref front() { return (*_range).front; } - static if (is(typeof((*(cast(const R*)_range)).front))) @property auto front() const + static if (is(typeof((*(cast(const R*)_range)).front))) + @property auto ref front() const { return (*_range).front; } - static if (is(typeof((*_range).front = (*_range).front))) @property auto front(ElementType!R value) + static if (hasAssignableElements!R && !hasLvalueElements!R) + @property auto front(ElementType!R value) { return (*_range).front = value; }