I see the use of flip-flop in doc.perl6.org, see the code below :
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
my @codelines = gather for $excerpt.lines {
take $_ if "=begin code" ff "=end code"
}
# this will print four lines, starting with "=begin code" and ending with
# "=end code"
.say for @codelines;
=begin code
This code block is what we're after.
We'll use 'ff' to get it.
=end code
=begin code
I want this line.
and this line as well.
HaHa
=end code
=begin code
Let's to go home.
=end code
I want to save the lines between =begin code
and =end code
into separate arrays, such as this:
['This code block is what we're after.', 'We'll use 'ff' to get it.']
['I want this line.', 'and this line as well.', 'HaHa']
['Let's to go home.']
I know grammar can do this, but I want to know if there is a beter way?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With