【问题标题】:Obtain a switch/case behaviour in Perl 5在 Perl 5 中获取 switch/case 行为
【发布时间】:2021-12-31 16:47:25
【问题描述】:

有没有在 Perl 5 中创建 case 或 switch 语句的简洁方法?在我看来,它们应该包括版本 6 的开关..

我需要在脚本中使用这种控制结构,而且我听说您可以导入“开关模块”。但是如何在没有导入的情况下实现它以最小化依赖并获得可移植性?

【问题讨论】:

  • Switch 模块是 Core Perl 的一部分,从 5.8 开始就有,所以不存在依赖问题;但是,它确实有一些错误和限制。有关更多信息,请参阅文档,或者更好的是,升级到 5.10 并获得 switch 语句、智能匹配运算符、说函数、状态变量、许多正则表达式改进、定义或 (//)、堆叠文件测试运算符 (- r -x $file 而不是 -r $file && -x _),以及许多其他改进。
  • @lurks,你可能想选择一个不同的答案。
  • this perlfaq7 question。 (不要使用 given/when 或 Switch 模块。)

标签: perl switch-statement


【解决方案1】:

如果您使用的是 Perl 5.10,您有 given/when 这是一个 switch 语句(注意,它可以做的不仅仅是与正则表达式比较,请阅读链接文档以了解其全部潜力):

#or any of the dozen other ways to tell 5.10 to use its new features
use feature qw/switch/; 

given($string) {
    when (/^abc/) { $abc     = 1; }
    when (/^def/) { $def     = 1; }
    when (/^xyz/) { $xyz     = 1; }
    default       { $nothing = 1; }
}

如果您使用的是 Perl 5.8 或更早版本,则必须使用 if/elsif/else 语句:

if    ($string =~ /^abc/) { $abc     = 1; }
elsif ($string =~ /^def/) { $def     = 1; }
elsif ($string =~ /^zyz/) { $xyz     = 1; }
else                      { $nothing = 1; }

或嵌套condition operators (?:):

$string =~ /^abc/ ? $abc     = 1  :
$string =~ /^def/ ? $def     = 1  :
$string =~ /^xyz/ ? $xyz     = 1  :
                    $nothing = 1;

Core Perl (Switch) 中有一个模块,它通过 source filters 为您提供虚假的 switch 语句,但我的理解是 fragile

use Switch;

switch ($string) {
    case /^abc/ {
    case /^abc/ { $abc     = 1 }
    case /^def/ { $def     = 1 }
    case /^xyz/ { $xyz     = 1 } 
    else        { $nothing = 1 }
}

或替代语法

use Switch 'Perl6';

given ($string) {  
    when /^abc/ { $abc     = 1; }
    when /^def/ { $def     = 1; }
    when /^xyz/ { $xyz     = 1; }
    default     { $nothing = 1; }
}

【讨论】:

  • Perl 5.10 版本设置 $_ 就像它在 Perl 6 中所做的那样。Switch 模块的给定/何时没有。
  • 'when' 是一个隐式的 '~~' 运算符,因此不仅允许使用正则表达式。
  • given/when 是一项被视为失败的实验性功能。它不应该被使用,因为它可以在没有警告的情况下被删除/更改。 /// Switch 模块使用源过滤器并自己解析 Perl 代码。这会导致非常奇怪的错误消息,基本上无法理解。也应该避免。
  • 我同意给定/何时是邪恶的。它在代码中创建了令人讨厌的错误,该代码在另一个 Perl 构建下运行良好(在两个环境中都使用 v5.10.1)。我来这里是为了寻找官方的替代品,如果存在的话。
【解决方案2】:

Programming Perl 中的建议是:


for ($string) {
    /abc/ and do {$abc    = 1; last;};
    /def/ and do {$def    = 1; last;};
    /xyz/ and do {$xyz    = 1; last;};
    $nothing = 1;
}

【讨论】:

  • 来自文档:“do BLOCK 不算作循环,因此循环控制语句nextlastredo 不能用于离开或重新启动块。” perldoc.perl.org/functions/do.html 所以last 至少看起来没必要?
  • do 块不是循环的一部分,但last 指的是for 循环,而不是do
  • 这还是有given/when的危险,没有警告。见stackoverflow.com/q/71010648/3092394
【解决方案3】:

我喜欢的等效解决方案是dispatch table

my $switch = {
  'case1' => sub { print "case1"; },
  'case2' => sub { print "case2"; },
  'default' => sub { print "unrecognized"; }
};
$switch->{$case} ? $switch->{$case}->() : $switch->{'default'}->();

【讨论】:

  • 短:($switch->{$case} || $switch->{default})->()
  • 这太美了!
  • $case 的连接变量使得这对于复杂的逻辑非常有效
【解决方案4】:

关于核心Switch 模块的简短评论已在答案中多次提及。有问题的模块依赖于源过滤器。除其他外,这可能会导致报告错误的错误行。太糟糕了,以至于没有一个核心开发人员真正记得或关心记住为什么它首先被接受到 perl 核心中。

此外,Switch.pm 将是第一个从 perl 核心中删除的 Perl 模块。 perl 的下一个主要版本 5.12.0 仍将包含它,尽管带有弃用警告。如果您从 CPAN 显式安装 Switch.pm,该弃用警告将消失。 (你得到你想要的。)在下一个版本 5.14 中,Switch.pm 将从核心中完全删除。

【讨论】:

    【解决方案5】:
    print("OK : 1 - CANCEL : 2\n");
    my $value = <STDIN>;
    SWITCH: {
        ($value == 1) && last(SWITCH);
        ($value == 2) && do {print("Cancelled\n"); exit()};
        print("??\n");
    }
    

    【讨论】:

    • 请考虑为您的答案添加一些描述。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多