New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix issue 5968 #1186
Fix issue 5968 #1186
Conversation
/** | ||
Returns $(D true) if $(D obj) is $(D null). | ||
*/ | ||
bool isNull(T)(auto ref RefCounted!T obj) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this function in module scope? Shouldn't it be put in the RefCounted implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is to not hide a method that the wrapped type might have with the same name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra DDoc explaining why this function is written the way it is, as well as a recommendation to NOT use it UFCS-style would help. I suppose using it UFCS style will call T
's member IsNull
with higher priority... correct?
This range appears to be non-transient safe, as it saves .front in _model and assumes that it will not change upon calling popFront. To be transient-safe, a forward range is required, and .save should be used in lieu of assigning .front to a temporary. In the case of an input range, transient-safety is impossible, so we can ignore that case. |
Actually, wait, this range requires a forward range. If R is not a forward range, the ctor of the outer range will fail (it calls data.save where data is of type R). |
ping |
oops sorry didn't see your comments, will look at this |
ping |
This seems to duplicate the functionality of #1453 's chunkBy (or the other way around). Implementation aside, the only difference I see is having a unary pred vs a binary pred. Right? IMO, for something that groups an arbitrary amount of elements together, it makes more sense to have a unary evaluation, than pair-wise comparison. I mean: goupBy!"toLower(a)"("sdqfkhUPIUmKSJHmgfue");
//vs
goupBy!"toLower(a) == toLower(b)"("sdqfkhUPIUmKSJHmgfue"); |
Although grouping after sorting is a common pattern, the range doesn't | ||
have to be sorted or structured in a particular way. | ||
*/ | ||
struct GroupBy(alias pred, R) if (isForwardRange!R) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current stance in phobos is to make the struct type private, and to only provide the adapter function that returns an auto
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK
Doesn't this implementation imply that doing a What's wrong with the approach taken by |
Yah, it would be fair to say #1453 is the duplicate seeing as this predates it by 5 months. We'll choose the better one but it's a bit of a bummer to see there's duplication of work. Fortunately it's not frequent, but I wonder what to do to prevent such.
groupBy!"toLower(a)"("sdqfkhUPIUmKSJHmgfue");
//vs
groupBy!"toLower(a) == toLower(b)"("sdqfkhUPIUmKSJHmgfue"); Yah, I think unary predicates are nice as well. But the entire Phobos was designed with binary predicates, e.g. we use Also, binary predicates may be more powerful than unary predicates. For example, to find sorted runs in a range one may write: groupBy!"a <= b"(range); I don't think that's expressible with unary predicates. |
I'll look into this.
Laziness is essential here seeing as a group may span the entire range, or an arbitrarily long subset of it. Making a O(n) pass just to |
I don't think that's the case, or I'm not sure I get what the failure scenario is. Here's a preexisting unittest (after I changed it to take in the
|
It works because slices are value semantic, but will fail with a reference semantic range, or a simple input range. auto myGroupBy = myRefRange.groupBy();
auto myFirstGroup = myGroupBy.front;
myGroupBy.popFront();
writeln(myFristGroup.front); Here, the This puts us in the awkard position (common to all functions that split a range), to operate exclusively on forward ranges (and not accept pure input), or to accept input ranges, but have a transient behavior (like |
A fundamental issue we have to deal with when working with range-splitting functions, is that because the result is a range of ranges, it forces you to either do duplicate work (unless your range has slicing), or make subranges transient. This applies not just to this function (or Consider this. If the given range R doesn't have slicing, then the only way to compute the subranges is to iterate over each element and evaluate some kind of predicate that tells you where the subrange ends. The problem is, if you don't operate on a |
You can count the elements, and return a |
@monarchdodra Good point! Though you still have to pop through all the elements of the original range twice, if the caller wants to process every element. So there's still some overhead to pay for as opposed to directly iterating over the original range and detecting subrange boundaries in-situ instead of using a range adaptor. |
Yah, so that's why the present code requires a forward range. My question is, do you see any correctness issue with it? (I think it saves wherever it needs.)
Again, counting the elements in the group as the first step in starting a new group is quite a drag. First off, it does nothing in the way of addressing the input range issue, so it has zero advantage over the present code. Second, a group may span essentially the entire range and so we can't masquerade an O(n) operation as a O(1) step, even though that would be amortized later. So the questions here are:
|
Where is the duplicate work here? |
I need to review what your code does in more details. Actually, I think I had misunderstood what it does. I think we may want to study it real closely: If it works, it may be something we want to push out to our other RoR's. In any case, I think you need to call save as you return front, or a usecase as trivial as
Also, keep in mind that with your design, |
Possibly. It's not trivial but effective: ranges share information about a "serial number" and its evolution to eliminate duplicate O(n) advances.
That's is a problem on the caller side. The call equal(r, r) will fail with many reference ranges.
The better goal is to not produce garbage I think. Merge now worry about input ranges later? We can always expand capabilities without breaking compatibility. |
Well... I really meant In that case, I think it looks good in the broad lines. I'll give it a fuller review when I have the time. |
Thanks! Quick, before it bitrots :o) |
destroy(this); | ||
_refCounted._store = null; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is issue 9636 (https://d.puremagic.com/issues/show_bug.cgi?id=9636), which is "controversial", as it changes the (already existing) semantic of what Nullable!T n; n = null;
, if T
happens to be a type for which = null;
already means something. EG: A Nullable!(int*)
...
If you still believe in this change, I'd recommend you at least give it its own pull.
As quick as I can. But this pull should have no conflict, and is now on my list of pull to review (and to pull), so there is little to no chances it will rot. |
@monarchdodra ok I undid all changes to RefCounted. |
@property bool empty() | ||
{ | ||
auto result = _thisGroup.empty | ||
|| !binaryFun!pred(_thisGroup.front, _model); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You compare with _model
here, but you never update _model
: You just initialize it once. This would be fine for a pred with equivalence
, but you sold byGroup
as being able to return, say "Groups of sorted items with a <= b
". However, with the current scheme (I think, not actually tested), you'll only return groups where every item is bigger than the first, not the previous.
Could you add a sorted unittest? Say:
[1, 3, 2, 4, 5, 1].byGroup!"a < b"().equal([[1, 3], [2, 4, 5], [1]]);
I think this fails with the current scheme...
This works as intended, but raises an interesting question - please don't pull yet. Will ask in the forum. |
Hum... Your "GroupBy" holds both a "Group", and a "SharedAllGroups". This gives it an ambiguous mix of both value and reference semantics. EG: void main()
{
auto a = groupBy([1, 3, 2, 2, 3, 3, 4, 4]);
auto b = a;
b.popFront();
b.popFront();
writeln(a);
}
That's just strange. That said, why not just store your Speaking of my above "class" comment, it might even be simplest to make all of I also want to point out that this range would be one the first reference semantic range in all of phobos! I think this is a good initiative. |
I get |
That is odd, since I duped your branch :/ well, no matter. |
void popFront() | ||
{ | ||
// Walk the current group through its end | ||
for (;; _front.popFront) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get what this is trying to do, but if R
is a "value range" (99% of the time), then this is duplicate work, since _front
will never actually be already empty.
@andralex - status on this one? |
For other readers: relevant thread is http://forum.dlang.org/post/l9ooqk$1596$1@digitalmars.com I think this should be closed in favor of #1453 and any missing functionality added there (as it has a more "modern" implementation). @andralex any objections? |
@@ -3726,6 +3726,192 @@ unittest | |||
} | |||
} | |||
|
|||
/** | |||
Given a forward range $(D r), returns a range of ranges that group adjacent |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Giten a forward range $(D r), this returns a range.
Added a real groupBy facility. Making it single-pass was interesting.