Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions changelog.dd
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -327,6 +329,15 @@ static assert( isFinal!(C.ff));
-------
)

$(LI $(LNAME2 generate, `std.range.generate` fixed to be a proper range.)
Copy link
Contributor

@JackStouffer JackStouffer Jul 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a note here about following the clarified range rules with a link, when #4511 is pulled

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most expect ranges to behave this way, the updating of the rules is just a formal presentation of what everyone already knows. So I don't think a link is necessary.

$(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:
Expand Down
102 changes: 75 additions & 27 deletions std/range/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Params: section is missing now?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not needed, basically said the same thing as Returns section.

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;
}

///
Expand Down Expand Up @@ -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);
Expand All @@ -3231,18 +3225,41 @@ private:
else
alias fun = Fun[0];

enum returnByRef_ = (functionAttributes!fun & FunctionAttribute.ref_) ? true : false;
static if (returnByRef_)
ReturnType!fun *elem_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Space between required if (. This is why Travis complains.

Likewise, at least one if and one foreach below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. that is so not clear from the error message 😄

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
Expand Down Expand Up @@ -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),
Expand Down