爆发的perl foreach循环问题

时间:2013-11-04 18:59:25

标签: perl foreach

我正在写一个小的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 ;  

2 个答案:

答案 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

eachkeys