在Perl 6中使用触发器操作符

时间:2018-03-14 14:39:07

标签: perl6

我在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.']

我知道语法可以做到这一点,但我想知道是否有更好的方式?

7 个答案:

答案 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与共享相同迭代器的序列 这是有效的,因为formap更渴望。

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