Perl 6中触发器运算符的使用
The use of flip-flop operator in Perl 6
我在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.']
我知道语法可以做到这一点,但我想知道是否有更好的方法?
您需要指定不包含匹配值。您可以通过将 ^
添加到要排除的运算符的一侧来执行此操作。在这种情况下,它是运算符的两侧。
您还需要收集最多的值以将它们组合在一起。在这种情况下,最简单的方法是在比赛之间将其关闭。
(如果您想要包含端点,则需要更多思考才能正确完成)
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
不会。
您也可以使用古老的正则表达式:
say ( $excerpt ~~ m:s:g{\=begin code\s+(.+?)\s+\=end code} ).map( *.[0] ).join("\n\n")
s
用于重要的空格(不是真正需要的),g
用于提取所有匹配项(不是第一个),.map
遍历返回的 Match object 和提取第一个元素(它是一个包含整个匹配代码的数据结构)。这将创建一个最终打印的列表,每个元素由两个 CR 分隔。
另一个answer in reddit by 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.
使用语法:
#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.",)
-----------------------------------
所以它按预期工作。
可能是另一种解决方案,使用rotor
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;
输出:
(("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.",)).Seq
另一个答案:
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;
}
使用 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
我在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.']
我知道语法可以做到这一点,但我想知道是否有更好的方法?
您需要指定不包含匹配值。您可以通过将 ^
添加到要排除的运算符的一侧来执行此操作。在这种情况下,它是运算符的两侧。
您还需要收集最多的值以将它们组合在一起。在这种情况下,最简单的方法是在比赛之间将其关闭。
(如果您想要包含端点,则需要更多思考才能正确完成)
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
不会。
您也可以使用古老的正则表达式:
say ( $excerpt ~~ m:s:g{\=begin code\s+(.+?)\s+\=end code} ).map( *.[0] ).join("\n\n")
s
用于重要的空格(不是真正需要的),g
用于提取所有匹配项(不是第一个),.map
遍历返回的 Match object 和提取第一个元素(它是一个包含整个匹配代码的数据结构)。这将创建一个最终打印的列表,每个元素由两个 CR 分隔。
另一个answer in reddit by 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.
使用语法:
#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.",)
-----------------------------------
所以它按预期工作。
可能是另一种解决方案,使用rotor
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;
输出:
(("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.",)).Seq
另一个答案:
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;
}
使用 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