Skip to content

Commit

Permalink
Allow for .skip(produce,skip,produce,skip,...) in 6.e
Browse files Browse the repository at this point in the history
Inspired by https://stackoverflow.com/questions/73175664/whats-an-elegant-way-to-return-a-list-without-the-nth-element

As answer to that question: @b.skip($n,1)

Some examples:

    say (^20).skip(5,3,3,6);     # (0 1 2 3 4 8 9 10 17 18 19)
    say (^20).skip(0,5,3);       # (5 6 7)
    say (^20).skip(|(2,3) xx *); # (0 1 5 6 10 11 15 16)

A * (or no value) on a place for production, means produce the rest.
A * (or no value) on a place for skipping, means stop producing altogether.
  • Loading branch information
lizmat committed Jul 30, 2022
1 parent b9c90bc commit 86d0c58
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 0 deletions.
7 changes: 7 additions & 0 deletions src/core.e/Any-iterable-methods.pm6
Expand Up @@ -9,6 +9,13 @@ augment class Any {
multi method snip(Any:D: *@conditions) {
Seq.new: Rakudo::Iterator.Snip(@conditions, self.iterator)
}

multi method skip(Iterable:D $skips) {
Seq.new: Rakudo::Iterator.Skipper: self.iterator, $skips.iterator
}
multi method skip(*@skips) {
self.skip(@skips)
}
}

proto sub rotor(|) {*}
Expand Down
68 changes: 68 additions & 0 deletions src/core.e/Rakudo/Iterator.pm6
Expand Up @@ -54,6 +54,74 @@ augment class Rakudo::Iterator {
proto method Snip(|) {*}
multi method Snip(\test, $iterator) { Snip.new: (test,), $iterator }
multi method Snip(@tests, $iterator) { Snip.new: @tests, $iterator }

# Return an iterator that will skip / produce values as specified by
# another iterator.
my class Skipper does Iterator {
has $!values;
has $!skips;
has uint $!produce;

method !SET-SELF($values, $skips) {
if nqp::eqaddr((my $produce := $skips.pull-one),IterationEnd) {
$values # nothing to skip
}
else {
$!values := $values;
$!skips := $skips;
$!produce = $produce;
self
}
}

method new($values, $skips) {
nqp::create(self)!SET-SELF($values, $skips)
}

method pull-one() {

# Still something to produce, so produce
if $!produce {
--$!produce;
$!values.pull-one
}

# Skip rest if no skipper or *
elsif nqp::eqaddr((my $skipper := $!skips.pull-one),IterationEnd)
|| nqp::istype($skipper,Whatever) {
IterationEnd
}

# Skip a number
else {
my uint $to-skip = $skipper;
nqp::until(
nqp::isle_i($to-skip,0)
|| nqp::eqaddr($!values.pull-one,IterationEnd),
--$to-skip
);

# not enough values to skip, so we're done
if $to-skip {
IterationEnd
}

# find out how many values to produce next
else {
$!produce = nqp::eqaddr(
(my $produce := $!skips.pull-one),
IterationEnd
) || nqp::istype($produce,Whatever)
?? -1 # produce the rest
!! $produce - 1;
$!values.pull-one
}
}
}
}

proto method Skipper(|) {*}
multi method Skipper(\values, \skips) { Skipper.new: values, skips }
}

# vim: expandtab shiftwidth=4

0 comments on commit 86d0c58

Please sign in to comment.