Perl 使用触发器操作符跟踪基于缩进的状态
我试图熟悉触发器操作符,这样在进行有状态循环时,我可以将其作为额外的抽象在头脑中,即使教科书式的状态机在这种情况下工作得非常好(而且冗长且变量丰富)。我想跟踪缩进,似乎我仍然需要在每个if块的开头手动调整缩进,在这种情况下我称之为缩进触发器,对吗? 以下是我的想法: 节目:Perl 使用触发器操作符跟踪基于缩进的状态,perl,Perl,我试图熟悉触发器操作符,这样在进行有状态循环时,我可以将其作为额外的抽象在头脑中,即使教科书式的状态机在这种情况下工作得非常好(而且冗长且变量丰富)。我想跟踪缩进,似乎我仍然需要在每个if块的开头手动调整缩进,在这种情况下我称之为缩进触发器,对吗? 以下是我的想法: 节目: use v5.20; use strict; use warnings; my $shiftwidth = 3; # block_rx: start of indented block marker, without l
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
Mark Twain
Jane Austen
Mark Twain
Jane Austen
Mark Twain
Klaus Teuber
Jane Austen
Mark Twain
Klaus Teuber
Jane Austen
实际产出:
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
Mark Twain
Jane Austen
Mark Twain
Jane Austen
Mark Twain
Klaus Teuber
Jane Austen
Mark Twain
Klaus Teuber
Jane Austen
如果我不必在循环中手动调整
$level
,那也很好。带有动态操作数的触发器运算符使用起来很棘手,可能无法达到预期效果。Perl为代码中出现的每个触发器运算符维护一个“状态”,而不是为作为操作数提供给触发器运算符的每个表达式维护一个单独的状态
考虑以下代码:
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>
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>
但这一条(用每一行输入定义新函数)并不:
sub fff { my $tag = shift; sub { m[<$tag>] .. m[</$tag>] } }
while (<DATA>) {
print "FOO:$_" if fff("foo")->();
print "BAR:$_" if fff("bar")->();
}
__DATA__
...
与
(不确定
s/*E//r
是做什么用的)使用严格和警告?是否将级别初始化为0?锚定触发器左操作数正则表达式?@ysth:对不起,我现在解决了这些问题,并使我的问题更清楚。当满足触发器条件时,触发器的字符串返回值以“E0”结尾;请看(但我不确定这对OP的解决方案有何影响)注意,仅仅一个新的匿名函数是不够的;它必须是一个闭包(参考至少一个外部词法)才能获得它自己的触发器状态数据(至少这是我的记忆),谢谢你的回答!我仍在阅读,首先让我说我使用了s/*E//r
,因为我认为这是使结束值返回false(“0”)从而排除它的最简单方法,因为我希望while循环中的内部条件仅在行仍然缩进时匹配。这很有效,非常酷!在最后一段代码中,它仍然必须是三个点,因此sub{/^$expr1/..!/^$expr2/}
,然后它直接工作。好的,我将切换回..
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__
...
...
return ((/^$indent$block_rx/) ... (!/^$inner_indent/)) =~ s/.*E//r;
my %FF;
sub flipflopfunc {
my ($expr1,$expr2) = @_;
return $FF{$expr1}{$expr2} //=
sub { /^$expr1/ ... !/^$expr2/ };
}
...
return flipflopfunc("$indent$block_rx",$inner_indent)->() =~ s/.*E//r;