1

我正在编写一个小的 perl 脚本,主要是为了学习该语言。基本上它有一个动作调度表。根据用户输入,任何一个目标动作都会被调用。每个动作都是一个小的、独立的实用函数(比如打印时间),它让我能够探索和学习 perl 的不同方面。

我遇到了调度机制的问题。该脚本在一个连续循环中运行,每次都接受用户的操作请求。将此输入与每个可用操作的正则表达式进行比较。如果存在匹配,则执行该操作并跳出匹配循环以读取用户的下一个请求。我面临的问题是,如果我两次请求相同的操作,则第二次不匹配。如果我在匹配后立即打印调度表,则匹配的操作条目似乎丢失了。如果我不断请求相同的操作,它仅适用于备用调用。如果我避免使用“最后一个标签”,它可以正常工作。

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 ;  
4

2 回答 2

5

您偶然发现了each操作员的一些微妙行为。通过在操作员用尽 的键值对之前跳出循环(使用last ExecAction),下一次调用将尝试检索不同的键值对。由于没有一个(数据结构的每个元素只定义了一个键值对),因此返回一个空列表,并跳过循环的内容。each%{@$Action[2]}each %{@$Action[2]}@Actioneachwhile

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

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

我认为将哈希显式复制到临时变量也可以:

        my %action_hash = %{@$Action[2]};
        while (($RegEx, $Function) = each %action_hash) {
           ...
        }
于 2013-11-04T19:32:05.713 回答
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

each,keys

于 2013-11-04T19:34:27.557 回答