Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

Commit

Permalink
efficient Range unions for #7435
Browse files Browse the repository at this point in the history
  • Loading branch information
gavinking committed Nov 19, 2018
1 parent 45a4ed1 commit 817ad5b
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 3 deletions.
62 changes: 62 additions & 0 deletions language/src/ceylon/language/Range.ceylon
Original file line number Diff line number Diff line change
Expand Up @@ -88,4 +88,66 @@ class Range<Element>()
"Returns the range itself, since a range cannot contain
duplicate elements."
shared actual Range<Element> distinct => this;

shared actual
Set<Element|Other&Object> union<Other>
(Collection<Other> collection)
=> switch (collection)
case (Range<Element>) Union<Element>(this, collection)
case (Union<Element>) Union<Element>(this, *collection.ranges)
else super.union(collection);

}

class Union<Element>(ranges)
satisfies Set<Element>
given Element satisfies Enumerable<Element> {

This comment has been minimized.

Copy link
@xkr47

xkr47 Nov 20, 2018

Contributor

Lazy implementation of range union/intersections etc seem to get a bit hairy, wondering whether it would make more sense to have it non-lazy instead.. Some years ago I have implemented non-lazy range union & intersection operations in C and JavaScript and ended up with a pretty optimized solution using a single array with starting points in even positions and ending points in odd.


shared Range<Element>+ ranges;

function small(Range<Element> r)
=> r.increasing then r.first else r.last;
function large(Range<Element> r)
=> r.increasing then r.last else r.first;

function before(Element x, Element y)
=> x.offsetSign(y)<0;

value minval = ranges.fold(small(ranges.first),
(p, r) => let (q=small(r))
if (before(p,q)) then p else q);
value maxval = ranges.fold(large(ranges.first),
(p, r) => let (q=large(r))
if (before(p,q)) then q else p);

iterator() => object satisfies Iterator<Element> {
variable value current = minval;
shared actual Element|Finished next() {
if (before(maxval, current)) {
return finished;
}
value result = current;
while (!before(maxval, current)) {
current = current.successor;
if (current in outer) {
break;
}
}
return result;
}
};

contains(Object element) => ranges.any((r) => element in r);

shared actual
Set<Element|Other&Object> union<Other>
(Collection<Other> collection)
=> switch (collection)
case (Range<Element>) Union<Element>(*ranges.withTrailing(collection))
case (Union<Element>) Union<Element>(*ranges.append(collection.ranges))
else super.union(collection);

equals(Object that) => (super of Set<Object>).equals(that);
hash => (super of Set<Object>).hash;

}
3 changes: 0 additions & 3 deletions language/test/lists.ceylon
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,6 @@ shared void lists() {

mappedArraySequence();

check((1..2)|(4..5) == set{1,2,4,5}, "union");
check((1..3)&(3..5) == set{3}, "intersection");

}

shared void mappedArraySequence() {
Expand Down
7 changes: 7 additions & 0 deletions language/test/range.ceylon
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,11 @@ shared void testRange() {
check((1..3)[1...]==2..3, "range span from");
check((1..3)[1:1]==2..2, "range measure");

check(2 in (1..2)|(4..5), "range union");
check(!3 in (1..2)|(4..5), "range union");
check(4 in (1..2)|(4..5), "range union");

check((1..2)|(4..5) == set{1,2,4,5}, "range union");
check((1..3)&(3..5) == set{3}, "range intersection");

}
7 changes: 7 additions & 0 deletions language/test/sequences.ceylon
Original file line number Diff line number Diff line change
Expand Up @@ -576,4 +576,11 @@ shared void sequences() {
assert (is String[4] split);
check(split == ["foo", "bar", "baz", "qux"], "tuple()");

check(2 in [1,2]|[4,5], "sequence union");
check(!3 in [1,2]|[4,5], "sequence union");
check(4 in [1,2]|[4,5], "sequence union");

check([1,2]|[4,5] == set{1,2,4,5}, "sequence union");
check([1,2,3]&[3,4,5] == set{3}, "sequence intersection");

}

1 comment on commit 817ad5b

@gavinking
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually this implementation is probably broken for recursive enumerable types like Byte. Might have to roll it back.

Please sign in to comment.