爆发的perl foreach循环问题

pro*_*ist 1 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 ;  
Run Code Online (Sandbox Code Playgroud)

mob*_*mob 5

您偶然发现了each运营商的一些微妙行为.通过last ExecActioneach运算符耗尽键值对之前中断循环(with )%{@$Action[2]},下一次调用each %{@$Action[2]}将尝试检索不同的键值对.由于没有一个(只为@Action数据结构的每个元素定义了一个键值对),each返回一个空列表,并while跳过循环的内容.

最简单的解决方法是each在每次使用之前重置与之关联的"迭代器" ,例如,通过调用keys:

        keys %{@$Action[2]};
        while (($RegEx, $Function) = each %{@$Action[2]})  
        {  
            ...
        }
Run Code Online (Sandbox Code Playgroud)

我认为显式将哈希复制到临时变量也是有效的:

        my %action_hash = %{@$Action[2]};
        while (($RegEx, $Function) = each %action_hash) {
           ...
        }
Run Code Online (Sandbox Code Playgroud)


ike*_*ami 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
Run Code Online (Sandbox Code Playgroud)

each, keys