Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
optimize [+]
There is now also a sum function which calls into a sum method.
Any.sum is 10-20% faster than the generic reduce routine, while
Range.sum is O(N) faster. :)
  • Loading branch information
TimToady committed Nov 15, 2015
1 parent 94ad45b commit 6d97011
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 4 deletions.
14 changes: 14 additions & 0 deletions src/core/Any.pm
Expand Up @@ -11,6 +11,9 @@ my class X::Subscript::Negative { ... }

my role Numeric { ... }

# We use a sentinel value to mark the end of an iteration.
my constant IterationEnd = Mu.CREATE;

my class Any { # declared in BOOTSTRAP
# my class Any is Mu {

Expand Down Expand Up @@ -407,6 +410,15 @@ my class Any { # declared in BOOTSTRAP
method print-nl() { self.print(self.nl-out) }

method lazy-if($flag) { self } # no-op on non-Iterables

method sum() {
my \iter = self.iterator;
my $sum = 0;
until (my \value = iter.pull-one) =:= IterationEnd {
$sum := $sum + value;
}
$sum;
}
}
Metamodel::ClassHOW.exclude_parent(Any);

Expand Down Expand Up @@ -463,6 +475,8 @@ multi sub elems($a) { $a.elems }
proto sub end(|) { * }
multi sub end($a) { $a.end }

sub sum(Any \SELF) { SELF.sum }

sub classify( $test, +items, *%named ) {
if %named.EXISTS-KEY("into") {
my $into := %named.DELETE-KEY("into");
Expand Down
3 changes: 0 additions & 3 deletions src/core/Iterator.pm
@@ -1,6 +1,3 @@
# We use a sentinel value to mark the end of an iteration.
my constant IterationEnd = Mu.CREATE;

# The Iterator role defines the API for an iterator and provides simple
# fallback implementations for most of it, so any given iterator can pick
# and choose what bits it can implement better for performance and/or
Expand Down
6 changes: 6 additions & 0 deletions src/core/Range.pm
Expand Up @@ -478,6 +478,12 @@ my class Range is Cool does Iterable does Positional {
method pop(|) is nodal {
X::Immutable.new(:typename<Range>, :method<pop>).throw
}

method sum() {
my ($start,$stop) = self.int-bounds;
my $elems = $stop - $start + 1;
($start + $stop) * $elems / 2;

This comment has been minimized.

Copy link
@b2gills

b2gills Nov 16, 2015

Contributor

Should probably be ($start + $stop) * $elems div 2; so that the output is an Int for Int based ranges.

There is another problem though, [+] 0.0 .. 10 and [+] 0e0 .. 10 now don't work at all.

Fixed with a662e02

}
}

sub infix:<..>($min, $max) is pure {
Expand Down
5 changes: 4 additions & 1 deletion src/core/metaops.pm
Expand Up @@ -171,7 +171,10 @@ multi sub METAOP_REDUCE_LEFT(\op) {
#?if jvm
my $ :=
#?endif
sub (+values) {

op =:= &infix:<+>
?? &sum
!! sub (+values) {
my \iter = values.iterator;
my \first = iter.pull-one;
return op.() if first =:= IterationEnd;
Expand Down

0 comments on commit 6d97011

Please sign in to comment.