我在doc.perl6.org中看到flip-flop的使用,请参阅以下代码:
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
我想将=begin code
和=end code
之间的行保存到单独的数组中,例如:
['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.']
我知道语法可以做到这一点,但我想知道是否有更好的方式?
答案 0 :(得分:9)
您需要指定不要包含匹配的值。您可以通过将^
添加到要排除的运算符旁边来执行此操作。在这种情况下,它是操作员的两面。
您还需要收集值以将其组合在一起。在这种情况下,最简单的方法是在比赛之间放弃 (如果您希望包含端点,则需要更多考虑才能使其正确)
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 @current {
take @current;
@current := []; # bind it to a new array
}
另一种方法是使用do for
与共享相同迭代器的序列
这是有效的,因为for
比map
更渴望。
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
接受一个序列并将其转换为另一个序列,但在尝试从序列中获取下一个值之前不会执行任何操作。
for
立即开始迭代,只有在你告诉它时停止。
因此即使在单个线程上运行,map
也会导致竞争条件,但for
不会。
答案 1 :(得分:2)
你也可以使用好的旧正则表达式:
say ( $excerpt ~~ m:s:g{\=begin code\s+(.+?)\s+\=end code} ).map( *.[0] ).join("\n\n")
s
用于显着的空格(不是真的需要),g
用于提取所有匹配(不是第一个),.map
遍历返回的Match object并提取第一个元素(它是一个包含整个匹配代码的数据结构)。这将创建一个最终打印的List,每个元素由两个CR分隔。
答案 2 :(得分:2)
由answer进行的另一个bobthecimmerian修改,出于完整性考虑,我将其复制到此处:
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;
}
输出为:
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.
答案 3 :(得分:2)
使用语法:
#use Grammar::Tracer;
#use Grammar::Debugger;
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
grammar ExtractSection {
rule TOP { ^ <section>+ %% <.comment> $ }
token section { <line>+ % <.ws> }
token line { <?!before <comment>> \N+ \n }
token comment { ['=begin code' | '=end code' ] \n }
}
class ExtractSectionAction {
method TOP($/) { make $/.values».ast }
method section($/) { make ~$/.trim }
method line($/) { make ~$/.trim }
method comment($/) { make Empty }
}
my $em = ExtractSection.parse($excerpt, :actions(ExtractSectionAction)).ast;
for @$em -> $line {
say $line;
say '-' x 35;
}
输出:
Here's some unimportant text.
-----------------------------------
This code block is what we're after.
We'll use 'ff' to get it.
-----------------------------------
More unimportant text.
-----------------------------------
I want this line.
and this line as well.
HaHa
-----------------------------------
More unimport text.
-----------------------------------
Let's to go home.
-----------------------------------
但是它包含不相关的行,基于@Brad Gilbert的解决方案,我将上述答案更新如下(并再次感谢):
#use Grammar::Tracer;
#use Grammar::Debugger;
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
grammar ExtractSection {
token start { ^^ '=begin code' \n }
token finish { ^^ '=end code' \n }
token line { ^^ \N+)> \n }
token section { <start> ~ <finish> <line>+? }
token comment { ^^\N+ \n }
token TOP { [<section> || <comment>]+ }
}
class ExtractSectionAction {
method TOP($/) { make @<section>».ast.List }
method section($/) { make ~«@<line>.List }
method line($/) { make ~$/.trim }
method comment($/) { make Empty }
}
my $em = ExtractSection.parse($excerpt, :actions(ExtractSectionAction)).ast;
for @$em -> $line {
say $line.perl;
say '-' x 35;
}
输出为:
$("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.",)
-----------------------------------
所以它按预期工作。
答案 4 :(得分:1)
这可能是另一种解决方案,请使用rotor
export default withStyles(styles)(connect(
mapStateToProps, mapDispatchToProps
)<React.ComponentClass>(NewComponent))
输出:
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 @sections =
gather for $excerpt.lines -> $line {
if $line ~~ /'=begin code'/ ff $line ~~ /'end code'/ {
take $line.trim;
}
}
my @idx = # gather take the indices of every `=begin code` and `=end code`
gather for @sections.kv -> $k, $v {
if $v ~~ /'=begin code'/ or $v ~~ /'end code'/ {
take $k;
}
}
my @r = # gather take the lines except every line of `=begin code` and `=end code`
gather for @sections.kv -> $k, $v {
if $v !~~ /'=begin code' | '=end code'/ {
take $v;
}
}
my @counts = @idx.rotor(2)».minmax».elems »-» 2;
say @r.rotor(|@counts).perl;
答案 5 :(得分:0)
另一个答案:
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
for $excerpt.comb(/'=begin code' \s* <( .+? )> \s+ '=end code' /) -> $c {
say $c;
say '-' x 15;
}
答案 6 :(得分:0)
使用comb运算符:
my $str = q:to/EOS/;
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 go home.
=end code
EOS
my token separator { '=begin code' \n | '=end code' \n }
my token lines { [<!separator> .]+ }
say $str.comb(
/
<lines> # match lines that not start with
# =begin code or =end code
<separator> # match lines that start with
# =begin code or =end code
<( # start capture
<lines>+ # match lines between
# =begin code and =end code
)> # end capture
<separator> # match lines that start with
# =begin code or =end code
/).raku;
输出:
("This code block is what we're after.\nWe'll use 'ff' to get it.\n", "I want this line.\nand this line as well.\nHaHa.\n", "Let's go home.\n").Seq