The use of flip-flop operator in Perl 6

You need to specify that you want the matched values to not be included. You do this by adding ^ to the side of the operator you want excluded. In this case it is both sides of the operator.

You also need to collect the values up to have the grouped together. The simplest way in this case is to put that off between matches.
(If you wanted the endpoints included it would require more thought to get it right)

my @codelines = gather {
  my @current;

  for $excerpt.lines {

    if "=begin code" ^ff^ "=end code" {

      # collect the values between matches
      push @current, $_;

    } else {
      # take the next value between matches

      # don't bother if there wasn't any values matched
      if @current {

        # you must do something so that you aren't
        # returning the same instance of the array
        take @current.List;
        @current = ();
      }
    }
  }
}

If you need the result to be an array of arrays (mutable).

if @current {
  take @current;
  @current := []; # bind it to a new array
}

An alternative would be to use do for with sequences that share the same iterator.
This works because for is more eager than map would be.

my $iterator = $excerpt.lines.iterator;

my @codelines = do for Seq.new($iterator) {
    when "=begin code" {
        do for Seq.new($iterator) {
            last when "=end code";
            $_<> # make sure it is decontainerized
        }
    }
    # add this because `when` will return False if it doesn't match
    default { Empty }
}

map takes one sequence and turns it into another, but doesn't do anything until you try to get the next value from the sequence.
for starts iterating immediately, only stopping when you tell it to.

So map would cause race conditions even when running on a single thread, but for won't.


You can also use good old regular expressions:

say ( $excerpt ~~ m:s:g{\=begin code\s+(.+?)\s+\=end code} ).map( *.[0] ).join("\n\n")

s for significant whitespace (not really needed), g for extracting all matches (not the first one), .map goes through the returned Match object and extracts the first element (it's a data structure that contains the whole matched code). This creates a List that is ultimatelly printed with every element separated by two CRs.


another answer in reddit by bobthecimmerian, i copy it here for completeness:

my $excerpt = q:to/END/;
Here's some unimportant text.
=begin code
This code block is what we're after.
We'll use 'ff' to get it.
=end code
More unimportant text.
=begin code
I want this line.
and this line as well.
HaHa
=end code
More unimport text.
=begin code
Let's to go home.
=end code
END

sub doSomething(Iterator $iter) { 
    my @lines = [];
    my $item := $iter.pull-one;
    until ($item =:= IterationEnd || $item.Str ~~ / '=end code' /) {
       @lines.push($item);
       $item := $iter.pull-one;
    }
    say "Got @lines[]";
}
my Iterator $iter = $excerpt.lines.iterator;
my $item := $iter.pull-one;
until ($item =:= IterationEnd) {
    if ($item.Str ~~ / '=begin code' /) {
       doSomething($iter);
    }
    $item := $iter.pull-one;
}

the output is:

Got This code block is what we're after. We'll use 'ff' to get it.
Got I want this line. and this line as well. HaHa
Got Let's to go home.

Tags:

Raku