Skip to content

Commit

Permalink
Implement Haskell's "span" in 6.e
Browse files Browse the repository at this point in the history
Inspired by https://stackoverflow.com/questions/72809469/in-raku-how-does-one-write-the-equivalent-of-haskells-span-function

This implements both the method as well as the sub version of "span".

For more flexibility, the condition can also be a List of conditions
(in the sub case), or just have multiple conditions (in the method case).

    .say for span * < 10, 2, 2, 2, 5, 5, 7, 13, 9, 6, 2, 20, 4;
    (2 2 2 5 5 7)
    (13 9 6 2 20 4)

    .say for span (* < 10, * < 20), 2, 2, 2, 5, 5, 7, 13, 9, 6, 2, 20, 4;
    (2 2 2 5 5 7)
    (13 9 6 2)
    (20 4)

    .say for (2, 2, 2, 5, 5, 7, 13, 9, 6, 2, 20, 4).span( * < 5, * < 10 );
    (2 2 2)
    (5 5 7)
    (13 9 6 2 20 4)

Span documentation: https://hackage.haskell.org/package/base-4.16.1.0/docs/Prelude.html#v:span
  • Loading branch information
lizmat committed Jul 1, 2022
1 parent 7cf2645 commit 9f44aca
Show file tree
Hide file tree
Showing 8 changed files with 84 additions and 3 deletions.
18 changes: 18 additions & 0 deletions src/core.e/Any-iterable-methods.pm6
@@ -1,3 +1,16 @@
augment class Any {
proto method span(|) {*}
multi method span(Any:D: &condition) {
Seq.new: Rakudo::Iterator.Span(&condition, self.iterator)
}
multi method span(Any:D: @conditions) {
Seq.new: Rakudo::Iterator.Span(@conditions, self.iterator)
}
multi method span(Any:D: *@conditions) {
Seq.new: Rakudo::Iterator.Span(@conditions, self.iterator)
}
}

proto sub rotor(|) {*}
multi sub rotor(Int:D $batch, \thing, *%_) {
thing.rotor($batch, |%_)
Expand All @@ -7,3 +20,8 @@ multi sub rotor(**@cycle-and-thing, *%_) {
@cycle-and-thing.tail.rotor(@cycle-and-thing.head(*-1), |%_)
}

proto sub span($, |) {*}
multi sub span(&condition, +values) { values.span(&condition) }
multi sub span(@conditions, +values) { values.span(@conditions) }

# vim: expandtab shiftwidth=4
59 changes: 59 additions & 0 deletions src/core.e/Rakudo/Iterator.pm6
@@ -0,0 +1,59 @@
augment class Rakudo::Iterator {
# Return an iterator that generates Lists of a given iterator
# and one or Callables with conditions. This is basically the
# Haskell "span" functionality.
my class Span does Iterator {
has $!tests;
has $!iterator;
has $!next;

method !SET-SELF(@tests, $iterator) {
$!tests := nqp::getattr(@tests,List,'$!reified');
$!iterator := $iterator;
$!next := $iterator.pull-one;
self
}

method new(@tests, $iterator) {
nqp::create(self)!SET-SELF(@tests, $iterator)
}

method pull-one() {
if nqp::eqaddr($!next,IterationEnd) {
IterationEnd
}
else {
my $buffer := nqp::create(IterationBuffer);
nqp::push($buffer,$!next);

my $iterator := $!iterator;
my $pulled;

if nqp::elems($!tests) {
my &test := nqp::shift($!tests);
nqp::until(
nqp::eqaddr(($pulled := $iterator.pull-one),IterationEnd)
|| nqp::isfalse(test($pulled)),
nqp::push($buffer,$pulled)
);

}
else {
nqp::until(
nqp::eqaddr(($pulled := $iterator.pull-one),IterationEnd),
nqp::push($buffer,$pulled)
);
}

$!next := $pulled;
$buffer.List
}
}
}

proto method Span(|) {*}
multi method Span(&test, $iterator) { Span.new: (&test,), $iterator }
multi method Span(@tests, $iterator) { Span.new: @tests, $iterator }
}

# vim: expandtab shiftwidth=4
2 changes: 1 addition & 1 deletion src/core.e/core_prologue.pm6
@@ -1,4 +1,4 @@
use nqp;
use MONKEY;

# This constant must specify current CORE revision
# Must preceede class declarations to allow correct recording of their respective language version.
Expand Down
1 change: 1 addition & 0 deletions t/02-rakudo/03-corekeys-6e.t
Expand Up @@ -433,6 +433,7 @@ my @expected = (
Q{&slurp},
Q{&so},
Q{&sort},
Q{&span},
Q{&splice},
Q{&split},
Q{&sprintf},
Expand Down
1 change: 1 addition & 0 deletions t/02-rakudo/03-corekeys.t
Expand Up @@ -798,6 +798,7 @@ my @allowed =
Q{&postcircumfix:<[; ]>},
Q{&postcircumfix:<{; }>},
Q{&rotor},
Q{&span},
Q{CORE-SETTING-REV},
Q{Grammar},
Q{PseudoStash},
Expand Down
1 change: 1 addition & 0 deletions t/02-rakudo/04-settingkeys-6e.t
Expand Up @@ -430,6 +430,7 @@ my %allowed = (
Q{&slurp},
Q{&so},
Q{&sort},
Q{&span},
Q{&splice},
Q{&split},
Q{&sprintf},
Expand Down
2 changes: 1 addition & 1 deletion t/02-rakudo/07-implementation-detail-6.e.t
Expand Up @@ -30,7 +30,7 @@ my @lower = ("",<<
prompt push put rand redo reduce rename repeated repl return
return-rw reverse rotor rindex rmdir roll roots rotate round roundrobin
run samecase samemark samewith say sec sech set shell shift sign
signal sin sinh sleep sleep-timer sleep-until slip slurp so sort
signal sin sinh sleep sleep-timer sleep-until slip slurp so sort span
splice split sprintf spurt sqrt squish srand subbuf-rw substr
substr-rw succeed sum symlink take take-rw tan tanh tc tclc trim
trim-leading trim-trailing truncate uc unimatch uniname
Expand Down
3 changes: 2 additions & 1 deletion tools/templates/6.e/core_sources
@@ -1,10 +1,11 @@
src/core.e/core_prologue.pm6
src/core.e/control.pm6
src/core.e/Any-iterable-methods.pm6
src/core.e/PseudoStash.pm6
src/core.e/Grammar.pm6
src/core.e/EXPORTHOW.pm6
src/core.e/Int.pm6
src/core.e/array_multislice.pm6
src/core.e/hash_multislice.pm6
src/core.e/Rakudo/Internals/Sprintf.pm6
src/core.e/Any-iterable-methods.pm6
src/core.e/Rakudo/Iterator.pm6

0 comments on commit 9f44aca

Please sign in to comment.