简体   繁体   English

使用触发器操作员跟踪基于缩进的状态

[英]Keep track of indentation-based state with flip-flop operator

I am trying to get familiar with the flip-flop operator, so I can have it as an additional abstraction in my head when doing stateful looping, even though a textbook-style state machine works perfectly well (and verbose and variable-rich) in such a case. 我试图熟悉触发器操作符,所以我可以在进行有状态循环时将它作为我头脑中的额外抽象,即使教科书式状态机运行良好(并且冗长和变量丰富)这种情况。 I want to keep track of indentation and it seems like I'd still need to manually adjust indentation at the start of every if block in whose condition I call my indenting flip-flop, right? 我想跟踪缩进,似乎我仍然需要在每个if块的开始处手动调整缩进,在这种情况下我称之为缩进触发器,对吧? Here's what I came up with: 这是我想出的:

Program : 计划

use v5.20;
use strict;
use warnings;

my $shiftwidth = 3;

# block_rx: start of indented block marker, without leading spaces
# Keeps state of indentation, which is increased on encountering block marker
# and decreased on matching outdent.
# Function should always get indentation level from context it was called in.
# Returns: true if in indented block, ^ff^, else false

sub indenting_flipflop {
    my $block_rx = $_[0];
    $_ = $_[1];
    my $level = $_[2];
    my $indent = indent($level);
    my $inner_indent = indent($level + 1);
    return ((/^$indent$block_rx/) ... (!/^$inner_indent/)) =~ s/.*E//r;
}

sub indent {
    return ' ' x ($shiftwidth * $_[0]);
}

while (<DATA>) {
    my $level = 0;
    if (indenting_flipflop('books', $_, $level)) {
        $level++;
        if (indenting_flipflop('book', $_, $level)) {
            $level++;
            if (/author: (.*)/) {
              say $1;
            }
        }
    }
}

__DATA__
books:
   book:
      author: Mark Twain
      price: 10.99
   game:
      author: Klaus Teuber
      price: 15.99
   book:
      author: Jane Austen
      price: 12.00

books:
   book:
      author: Mark Twain
      price: 10.99
   game:
      author: Klaus Teuber
      price: 15.99
   book:
      author: Jane Austen
      price: 12.00

Expected output : 预期产量

Mark Twain
Jane Austen
Mark Twain
Jane Austen

Actual output : 实际产量

Mark Twain
Klaus Teuber
Jane Austen
Mark Twain
Klaus Teuber
Jane Austen

It would also be nice, if I wouldn't have to adjust $level manually in the loop. 如果我不必在循环中手动调整$level ,那也很好。

Flip-flop operators with dynamic operands are tricky to use and may not do what you expect. 具有动态操作数的触发器操作符使用起来很棘手,可能无法达到预期效果。 Perl maintains a single "state" for each flip-flop operator that appears in the code, not a separate state for each expression supplied as operands to the flip-flop operator. Perl为代码中出现的每个触发器操作符保持单个“状态”,而不是作为触发器操作符的操作数提供的每个表达式的单独状态。

Consider this code: 考虑以下代码:

sub foo { m[<foo>] .. m[</foo>] }
sub bar { m[<bar>] .. m[</bar>] }

while (<DATA>) {    
    print "FOO:$_" if foo();
    print "BAR:$_" if bar();
}    
__DATA__
<foo>
   <bar>
      123
   </bar>
   <baz>
       456
   </baz>
</foo>

The output is: 输出是:

FOO:<foo>
FOO:   <bar>
BAR:   <bar>
FOO:      123
BAR:      123
FOO:   </bar>
BAR:   </bar>
FOO:   <baz>
FOO:       456
FOO:   </baz>
FOO:</foo>

So far, so good, right? 到目前为止,这么好,对吗? This approach won't scale well when there are 100 different tags to track instead of two, so let's try this code: 当有100个不同的标签要跟踪而不是两个时,这种方法不能很好地扩展,所以让我们试试这个代码:

sub ff { my $tag = shift; m[<$tag>] .. m[</$tag>] }
while (<DATA>) {
    print "FOO:$_" if ff("foo");
    print "BAR:$_" if ff("bar");
}
__DATA__
<foo>
   <bar>
      123
   </bar>
   <baz>
       456
   </baz>
</foo>

Now the output is 现在输出是

FOO:<foo>
BAR:<foo>
FOO:   <bar>
BAR:   <bar>
FOO:      123
BAR:      123
FOO:   </bar>
BAR:   </bar>

What happened? 发生了什么? BAR is always printed with the same lines as FOO , and the last line of output is the </bar> line, even though there is more data still enclosed in <foo></foo> tags. BAR总是使用与FOO相同的行打印,最后一行输出是</bar>行,即使还有更多数据仍包含在<foo></foo>标记中。

What happened is that the code contains a single flip-flop operator, defined in the ff subroutine, and this operator maintains a single state. 发生的事情是代码包含一个触发器操作符,在ff子例程中定义,并且该操作符保持单个状态。 The state changes to "true" when ff("foo") is called with the first line of input, and it remains "true" until it encounters input and an operand that satisfies the second expression in the flip-flop operator, which happens with the 4th line when ff("bar") is called. 当使用第一行输入调用ff("foo")时状态变为“true”,并且在遇到输入和满足触发器操作符中第二个表达式的操作数时它保持“真”调用ff("bar")时的第4行。 It is not maintaining separate state for foo tags and bar tags as the first example did. 正如第一个例子所做的那样, foo标签和bar标签没有保持单独的状态。

Passing different input to the indenting_flipflop function and expecting the flip-flop operator in that function to just operate on that kind of input will not work. 将不同的输入传递给indenting_flipflop函数并期望该函数中的触发器操作符仅对该类输入进行操作将不起作用。


Update : so this approach, defining a single new function for each tag, works: 更新 :所以这种方法,为每个标签定义一个新函数,工作原理:

sub fff { my $tag = shift; sub { m[<$tag>] .. m[</$tag>] } }
my $foo = fff("foo");
my $bar = fff("bar");
while (<DATA>) {
    print "FOO:$_" if $foo->();
    print "BAR:$_" if $bar->();
}
__DATA__
...

but this one (defining new functions with every line of input) does not: 但是这个(用每行输入定义新函数)不会:

sub fff { my $tag = shift; sub { m[<$tag>] .. m[</$tag>] } }
while (<DATA>) {
    print "FOO:$_" if fff("foo")->();
    print "BAR:$_" if fff("bar")->();
}
__DATA__
...

On the other other hand a memoized version of it would work: 另一方面,它的备忘版本将起作用:

my %FF;
sub fff { my $tag = shift; $FF{$tag} //= sub { m[<$tag>] .. m[</$tag>] } }
while (<DATA>) {
    print "FOO:$_" if fff("foo")->();
    print "BAR:$_" if fff("bar")->();
}
__DATA__
...

I'm still not convinced that flip-flop operators would add any value to this problem, but to find out you would have to use memoized flip-flop operator generating functions. 我仍然不相信触发器操作符会为这个问题增加任何值,但要发现你必须使用memoized触发器操作符生成函数。 Replace 更换

...
return ((/^$indent$block_rx/) ... (!/^$inner_indent/)) =~ s/.*E//r;

with

my %FF;
sub flipflopfunc {
    my ($expr1,$expr2) = @_;
    return $FF{$expr1}{$expr2} //= 
        sub { /^$expr1/ ... !/^$expr2/ };
}
...
return flipflopfunc("$indent$block_rx",$inner_indent)->() =~ s/.*E//r;

(not sure what the s/.*E//r is for) (不确定s/.*E//r是什么)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM