我正在写一个小的perl脚本,主要是为了学习这门语言。基本上它有一个动作调度表。根据用户输入,将调用任何一个目标操作。每个动作都是一个小的,独立的效用函数(比如打印时间),通过它可以让我探索和学习perl的不同方面。
我遇到了调度机制的问题。该脚本以连续循环运行,每次都收到用户对操作的请求。此输入将与每个可用操作的正则表达式进行比较。如果匹配,则执行该操作,并且它会从匹配循环中跳出以读取用户的下一个请求。我面临的问题是,如果我两次请求相同的操作,则第二次不匹配。如果我在匹配后立即打印调度表,则匹配的操作条目似乎丢失。如果我不断请求相同的操作,它只适用于备用调用。如果我避开“最后一个LABEL”,它的工作没有任何问题。
Perl版本是5.12.4(在Fedora 15上,32位)。以下是一个简化但完整的示例。我仍然是perl的初学者。如果它不符合僧侣的标准,请原谅:)请帮助找出这个代码的问题。非常感谢您的帮助。
#!/bin/env perl
use strict ;
use warnings ;
use Text::ParseWords ;
my @Actions ;
my $Quit ;
sub main
{
# Dispatch table
# Each row has [syntax, help, {RegExp1 => Function1, RegExp2 => Function2,...}]
# There can be multiple RegExps depending on optional arguments in user input
@Actions =
(
['first <value>', 'Print first', {'first (.*)' => \&PrintFirst} ],
['second <value>', 'Print second', {'second (.*)' => \&PrintSecond} ],
['quit', 'Exits the script', {'quit' => \&Quit} ]
) ;
my $CommandLine ;
my @Captures ;
my $RegEx ;
my $Function ;
while(!$Quit)
{
# Get user input, repeat until there is some entry
while(!$CommandLine)
{
print "\$ " ;
my $argline = <STDIN> ;
my @args = shellwords($argline) ;
$CommandLine = join (" ", grep { length() } @args) ;
}
# Find and execute the action matching given input
# For each entry in the @Actions array
ExecAction: foreach my $Action (@Actions)
{
# For each RegExp for that action (only 1 per action in this example)
while (($RegEx, $Function) = each %{@$Action[2]})
{
if (@Captures = $CommandLine =~ m/^$RegEx$/i)
{
print "Match : $RegEx\n" ;
&$Function(@Captures) ;
last ExecAction ; # Works if this line is commented
}
}
}
$CommandLine = undef ;
}
}
sub PrintFirst { print "first $_[0]\n" ; }
sub PrintSecond { print "second $_[0]\n" ; }
sub Quit { $Quit = 1 ; }
main ;
答案 0 :(得分:5)
您偶然发现了each
运营商的一些微妙行为。在last ExecAction
运算符耗尽each
的键值对之前突破循环(使用%{@$Action[2]}
),下一次调用each %{@$Action[2]}
将尝试检索不同的键值对。由于没有一个(只为@Action
数据结构的每个元素定义了一个键值对),each
返回一个空列表,以及while
的内容循环被跳过。
最简单的解决方法是在每次使用之前重置与each
相关联的“迭代器”,例如,通过调用keys
:
keys %{@$Action[2]};
while (($RegEx, $Function) = each %{@$Action[2]})
{
...
}
我认为明确地将哈希复制到临时变量也是有效的:
my %action_hash = %{@$Action[2]};
while (($RegEx, $Function) = each %action_hash) {
...
}
答案 1 :(得分:5)
如果你要打破each
循环,你需要重置哈希的迭代器
$ perl -E'
%h=(a=>4,b=>5,c=>6,d=>7);
while (my ($k, $v) = each %h) { last if ++$i == 2 }
keys %h if $ARGV[0];
while (my ($k, $v) = each %h) { say "$k:$v"; }
' 0
b:5
d:7
$ perl -E'
%h=(a=>4,b=>5,c=>6,d=>7);
while (my ($k, $v) = each %h) { last if ++$i == 2 }
keys %h if $ARGV[0];
while (my ($k, $v) = each %h) { say "$k:$v"; }
' 1
c:6
a:4
b:5
d:7