From 71a01e956f5ab91d64f31d8af8d4c9dfab4e392c Mon Sep 17 00:00:00 2001 From: Elizabeth Mattijsen Date: Thu, 20 Oct 2016 13:20:05 +0200 Subject: [PATCH] Introduce Iterator.skip-one A method that can be overriden if you're writing an iterator that can be significantly cheaper when skipping a generated value. Use case, the :10nth parameter with Str.match (aka, give me only the 10th match). By default, it is just a slightly more expensive .pull-one. Also use let Iterator.skip-at-least-pull-one use skip-one, as well as in Rakudo::Internals.SeqSkipNFromIterator . --- src/core/Iterator.pm | 27 ++++++++++++++++-------- src/core/Rakudo/Internals.pm | 41 ++++++++++++++++-------------------- 2 files changed, 36 insertions(+), 32 deletions(-) diff --git a/src/core/Iterator.pm b/src/core/Iterator.pm index b821b802148..dbad2a386d1 100644 --- a/src/core/Iterator.pm +++ b/src/core/Iterator.pm @@ -8,6 +8,14 @@ my role Iterator { # methods in this role, they'll all end up falling back to using this. method pull-one() { ... } + # Skip one value from the iterator. Should return a true-like value to + # indicate the skip was successful. Override this method if you can + # make an iterator that has significantly less to do when skipping a + # generated value. + method skip-one() { + nqp::not_i(nqp::eqaddr(self.pull-one,IterationEnd)) + } + # Has the iterator produce a certain number of values and push them into # the target. The only time the iterator may push less values than asked # for is when it reaches the end of the iteration. It may never push more @@ -68,17 +76,18 @@ my role Iterator { # Skip the given number of values produced before returning the next # pulled value. Given 0 it is an expensive way to do .pull-one - method skip-at-least-pull-one(\todrop) is raw { + method skip-at-least-pull-one(\toskip) is raw { nqp::stmts( - (my int $topull = todrop + 1), - nqp::while( - nqp::isge_i(($topull = nqp::sub_i($topull,1)),0), - nqp::if( - nqp::eqaddr((my $pulled := self.pull-one),IterationEnd), - ($topull = 0), - ) + (my int $left = toskip), + nqp::while( # skipping + nqp::isge_i(($left = nqp::sub_i($left,1)),0) && self.skip-one, + Nil ), - $pulled + nqp::if( + nqp::islt_i($left,0), # could skip all? + self.pull-one, + IterationEnd + ) ) } diff --git a/src/core/Rakudo/Internals.pm b/src/core/Rakudo/Internals.pm index 947d9909715..be1d2dd5c15 100644 --- a/src/core/Rakudo/Internals.pm +++ b/src/core/Rakudo/Internals.pm @@ -308,34 +308,29 @@ my class Rakudo::Internals { method new(\i,\s) { nqp::create(self)!SET-SELF(i,s) } method pull-one() is raw { nqp::if( - nqp::islt_i($!skipping,-1), - IterationEnd, # exhausted before - nqp::stmts( # not exhausted yet - nqp::if( + $!iterator, + nqp::stmts( + nqp::while( nqp::isgt_i($!skipping,0), - nqp::until( # still skipping - nqp::iseq_i($!skipping,0), - nqp::if( - nqp::eqaddr($!iterator.pull-one,IterationEnd), - nqp::stmts( # exhausted now - ($!skipping = -1), - (return IterationEnd) - ), - ($!skipping = nqp::sub_i($!skipping,1)), + nqp::if( + $!iterator.skip-one, + ($!skipping = nqp::sub_i($!skipping,1)), # skipped ok + nqp::stmts( # exhausted + ($!iterator := Mu), + (return IterationEnd) ) ) ), - nqp::stmts( # no longer skipping - nqp::if( - nqp::eqaddr( - (my $pulled := $!iterator.pull-one), - IterationEnd - ), - ($!skipping = -1), + nqp::if( # done skipping + nqp::eqaddr( + (my $pulled := $!iterator.pull-one), + IterationEnd ), - $pulled - ) - ) + ($!iterator := Mu) # exhausted + ), + $pulled + ), + IterationEnd # exhausted before ) } }.new(iterator,skipping))