diff --git a/changelog.dd b/changelog.dd index 71f127ec788..3485c95d61a 100644 --- a/changelog.dd +++ b/changelog.dd @@ -60,6 +60,8 @@ $(BUGSTITLE Library Changes, $(REF_ALTTEXT `curl_escape`, curl_escape, etc,c,curl), $(REF_ALTTEXT `curl_easy_unescape`, curl_easy_unescape, etc,c,curl), and $(REF_ALTTEXT `curl_unescape`, curl_unescape, etc,c,curl).) + $(LI $(RELATIVE_LINK2 generate, `std.range.generate` fixed to be a proper + range.)) ) $(BUGSTITLE Library Changes, @@ -327,6 +329,15 @@ static assert( isFinal!(C.ff)); ------- ) +$(LI $(LNAME2 generate, `std.range.generate` fixed to be a proper range.) + $(P $(XREF range, generate) was set up to return a different value on each + call to `front`. In addition, `popFront` did nothing. This means that + manipulation functions like $(XREF range, drop) did nothing. The new + version uses a one-element cache to meet the expectations of the range + definition. It preserves the ref-ness of the generator as well. + ) +) + ) Macros: diff --git a/std/range/package.d b/std/range/package.d index 13c2f430708..8dd84c12d7a 100644 --- a/std/range/package.d +++ b/std/range/package.d @@ -3146,41 +3146,36 @@ Take!(Repeat!T) repeat(T)(T value, size_t n) } /** -Given callable ($(REF isCallable, std,traits)) $(D fun), create as a range -whose front is defined by successive calls to $(D fun()). +Given callable ($(REF isCallable, std,traits)) `fun`, create as a range +whose front is defined by successive calls to `fun()`. This is especially useful to call function with global side effects (random functions), or to create ranges expressed as a single delegate, rather than -an entire $(D front)/$(D popFront)/$(D empty) structure. -$(D fun) maybe be passed either a template alias parameter (existing -function, delegate, struct type defining static $(D opCall)... ) or -a run-time value argument (delegate, function object... ). +an entire `front`/`popFront`/`empty` structure. +`fun` maybe be passed either a template alias parameter (existing +function, delegate, struct type defining `static opCall`) or +a run-time value argument (delegate, function object). The result range models an InputRange ($(REF isInputRange, std,range,primitives)). -The resulting range will call $(D fun()) on every call to $(D front), -and only when $(D front) is called, regardless of how the range is -iterated. -It is advised to compose generate with either -$(REF cache, std,algorithm,iteration) or $(REF array, std,array), or to use it in a -foreach loop. -A by-value foreach loop means that the loop value is not $(D ref). +The resulting range will call `fun()` on construction, and every call to +`popFront`, and the cached value will be returned when `front` is called. -Params: - fun = is the $(D isCallable) that gets called on every call to front. - -Returns: an $(D inputRange) that returns a new value generated by $(D Fun) on - any call to $(D front). +Returns: an `inputRange` where each element represents another call to fun. */ auto generate(Fun)(Fun fun) if (isCallable!fun) { - return Generator!(Fun)(fun); + auto gen = Generator!(Fun)(fun); + gen.popFront(); // prime the first element + return gen; } /// ditto auto generate(alias fun)() if (isCallable!fun) { - return Generator!(fun)(); + auto gen = Generator!(fun)(); + gen.popFront(); // prime the first element + return gen; } /// @@ -3219,7 +3214,6 @@ unittest format("%(%s %)", r); } -//private struct Generator(bool onPopFront, bool runtime, Fun...) private struct Generator(Fun...) { static assert(Fun.length == 1); @@ -3231,18 +3225,41 @@ private: else alias fun = Fun[0]; + enum returnByRef_ = (functionAttributes!fun & FunctionAttribute.ref_) ? true : false; + static if (returnByRef_) + ReturnType!fun *elem_; + else + ReturnType!fun elem_; public: /// Range primitives enum empty = false; - /// ditto - auto ref front() @property + static if (returnByRef_) { - return fun(); + /// ditto + ref front() @property + { + return *elem_; + } + /// ditto + void popFront() + { + elem_ = &fun(); + } + } + else + { + /// ditto + auto front() @property + { + return elem_; + } + /// ditto + void popFront() + { + elem_ = fun(); + } } - - /// ditto - void popFront() {} } @safe unittest @@ -3270,6 +3287,37 @@ public: assert(equal(generate(op).take(10), repeat(5).take(10))); } +// verify ref mechanism works +@system unittest +{ + int[10] arr; + int idx; + + ref int fun() { + auto x = idx++; + idx %= arr.length; + return arr[x]; + } + int y = 1; + foreach (ref x; generate!(fun).take(20)) + { + x += y++; + } + import std.algorithm.comparison : equal; + assert(equal(arr[], iota(12, 32, 2))); +} + +// assure front isn't the mechanism to make generate go to the next element. +@safe unittest +{ + int i; + auto g = generate!(() => ++i); + auto f = g.front; + assert(f == g.front); + g = g.drop(5); // reassign because generate caches + assert(g.front == f + 5); +} + /** Repeats the given forward range ad infinitum. If the original range is infinite (fact that would make $(D Cycle) the identity application),