我应该使用 Perl 的条件吗? : 运算符作为 switch / case 语句还是代替 if elsif?

发布于 2024-09-26 23:33:18 字数 3985 浏览 1 评论 0 原文

Perl 有一个 条件 运算符,与 C 的条件运算符

刷新一下,C 和 Perl 中的条件运算符是:

(test) ? (if test was true) : (if test was false)

如果与左值一起使用,您可以通过一个操作进行分配和测试:

my $x=  $n==0 ? "n is 0" : "n is not 0";

我正在阅读 Igor Ostrovsky 的博客 一种在基于 C 的语言中表达多子句 if 语句的巧妙方法< /a> 并意识到这在 Perl 中确实是一种“巧妙的方式”。

例如:(编辑:使用 Jonathan Leffler 的更具可读性的形式...)

# ternary conditional form of if / elsif construct:
my $s=
      $n == 0     ? "$n ain't squawt"
    : $n == 1     ? "$n is not a lot"
    : $n < 100    ? "$n is more than 1..."
    : $n < 1000   ? "$n is in triple digits"
    :               "Wow! $n is thousands!" ;  #default

这比许多人用 Perl 编写的内容更容易阅读: (编辑:在 rafi 的答案中使用了 cjm 更优雅的 my $t=do{ if }; 形式)

# Perl form, not using Switch or given / when
my $t = do {
    if    ($n == 0)   { "$n ain't squawt"        }
    elsif ($n == 1)   { "$n is not a lot"        }
    elsif ($n < 100)  { "$n is more than 1..."   }
    elsif ($n < 1000) { "$n is in triple digits" }
    else              {  "Wow! $n is thousands!" }
};

这里有什么陷阱或缺点吗?为什么我不以这种方式编写扩展条件形式而不是使用 if(something) { this } elsif(something) { that }?

条件运算符具有右结合性和低优先级。因此:

a ? b : c ? d : e ? f : g

被解释为:

a ? b : (c ? d : (e ? f : g))

我想如果您的测试使用优先级低于 ?: 的少数运算符之一,您可能需要括号。我认为您也可以将块放入带大括号的形式中。

我确实知道已弃用的 use Switch 或 Perl 5.10 的 given/when 构造,并且我并不是在寻找使用它们的建议。

这些是我的问题:

  • 你见过 Perl 中使用的这种语法吗?** 我没有,而且它也不在 perlopperlsyn 中作为 switch 的替代方法.

  • 以这种方式使用条件/三元运算符是否存在潜在的语法问题或“陷阱”? ?

  • 意见:对您来说更易读/理解吗?它与 Idiomatic Perl 一致吗?

-------- 编辑 --

我接受了 Jonathan Leffler 的回答,因为他向我指出了 Perl 最佳实践。相关部分是关于表格三元数的 6.17。这使我能够进一步研究其用途。 (如果你 Google Perl Tabular Ternaries,你可以看到其他注释。)

Conway 的两个例子是:

my $salute;
if ($name eq $EMPTY_STR) {
    $salute = 'Dear Customer';
}
elsif ($name =~ m/\A ((?:Sir|Dame) \s+ \S+)/xms) {
    $salute = "Dear $1";
}

elsif ($name =~ m/([^\n]*), \s+ Ph[.]?D \z/xms) {
    $sa1ute = "Dear Dr $1";
}
else {
    $salute = "Dear $name";
}

VS:

           # Name format...                            # Salutation...
my $salute = $name eq $EMPTY_STR                       ? 'Dear Customer'
           : $name =~ m/ \A((?:Sir|Dame) \s+ \S+) /xms ? "Dear $1"
           : $name =~ m/ (.*), \s+ Ph[.]?D \z     /xms ? "Dear Dr $1"
           :                                             "Dear $name"
           ;

我的结论是:

  • Conway 的 ?: 示例对我来说比 < code>if/elsif 形式,但我可以看到该形式如何变得难以理解。

  • 如果您有 Perl 5.13.1,请使用 my $t=do { 鉴于 { when } }; 作为赋值 拉菲已经完成了。我认为given/when< /code> 是现在最好的习惯用法,除非表格三元格式更适合您的特定情况。

  • 如果您有 Perl 5.10+,通常使用 given/when 而不是 Switch 或者如果您需要某种 case 类型开关。

  • 对于较早的 Perl,这是一种很好的简单替代形式或作为 case 语句的替代形式。我认为它比使用 Switch 更好。

  • 从右到左的关联性意味着表单是从下到上计算的。请记住,当使用...

Perl has a conditional operator that is the same a C's conditional operator.

To refresh, the conditional operator in C and in Perl is:

(test) ? (if test was true) : (if test was false)

and if used with an lvalue you can assign and test with one action:

my $x=  $n==0 ? "n is 0" : "n is not 0";

I was reading Igor Ostrovsky's blog on A neat way to express multi-clause if statements in C-based languages and realized this is indeed a "neat way" in Perl as well.

For example: (edit: used Jonathan Leffler's more readable form...)

# ternary conditional form of if / elsif construct:
my $s=
      $n == 0     ? "$n ain't squawt"
    : $n == 1     ? "$n is not a lot"
    : $n < 100    ? "$n is more than 1..."
    : $n < 1000   ? "$n is in triple digits"
    :               "Wow! $n is thousands!" ;  #default

Which reads a LOT easier than what many would write in Perl:
(edit: used cjm's more elegant my $t=do{ if }; form in rafi's answer)

# Perl form, not using Switch or given / when
my $t = do {
    if    ($n == 0)   { "$n ain't squawt"        }
    elsif ($n == 1)   { "$n is not a lot"        }
    elsif ($n < 100)  { "$n is more than 1..."   }
    elsif ($n < 1000) { "$n is in triple digits" }
    else              {  "Wow! $n is thousands!" }
};

Are there any gotchas or downside here? Why would I not write an extended conditional form in this manner rather than use if(something) { this } elsif(something) { that }?

The conditional operator has right associativity and low precedence. So:

a ? b : c ? d : e ? f : g

is interpreted as:

a ? b : (c ? d : (e ? f : g))

I suppose you might need parenthesis if your tests used one of the few operator of lower precedence than ?:. You could also put blocks in the form with braces I think.

I do know about the deprecated use Switch or about Perl 5.10's given/when constructs, and I am not looking for a suggestion to use those.

These are my questions:

  • Have you seen this syntax used in Perl?** I have not, and it is not in perlop or perlsyn as an alternate to switch.

  • Are there potential syntax problems or 'gotchas' with using a conditional / ternary operator in this way?

  • Opinion: Is it more readable / understandable to you? Is it consistent with Idiomatic Perl?

-------- Edit --

I accepted Jonathan Leffler's answer because he pointed me to Perl Best Practices. The relevant section is 6.17 on Tabular Ternaries. This allowed me to investigate the use further. (If you Google Perl Tabular Ternaries, you can see other comments.)

Conway's two examples are:

my $salute;
if ($name eq $EMPTY_STR) {
    $salute = 'Dear Customer';
}
elsif ($name =~ m/\A ((?:Sir|Dame) \s+ \S+)/xms) {
    $salute = "Dear $1";
}

elsif ($name =~ m/([^\n]*), \s+ Ph[.]?D \z/xms) {
    $sa1ute = "Dear Dr $1";
}
else {
    $salute = "Dear $name";
}

VS:

           # Name format...                            # Salutation...
my $salute = $name eq $EMPTY_STR                       ? 'Dear Customer'
           : $name =~ m/ \A((?:Sir|Dame) \s+ \S+) /xms ? "Dear $1"
           : $name =~ m/ (.*), \s+ Ph[.]?D \z     /xms ? "Dear Dr $1"
           :                                             "Dear $name"
           ;

My conclusions are:

  • Conway's ?: example is more readable and simpler to me than the if/elsif form, but I could see how the form could get hard to understand.

  • If you have Perl 5.13.1, use my $t=do { given { when } }; as an assignment as rafi has done. I think given/when is the best idiom now, unless the tabular ternary format is better for your particular case.

  • If you have Perl 5.10+ use given/when in general instead of Switch or if you need some sort of case type switch.

  • Older Perl's, this is a fine form for simple alternatives or as an alternate to a case statement. It is better than using Switch I think.

  • The right to left associativity means the form is evaluated bottom to top. Remember that when using...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

芯好空 2024-10-03 23:33:18

我在 perl 中见过这个习惯用法。作为三元运算符 ? : 是,嗯.. 一个运算符,它记录在 perlop 中,而不是 perlsyn

在我看来,这是一种惯用的 Perl,但 Perl 中这样做的主要目的似乎是避免缺乏适当的 switch 语句,同时不编写巨大的 if/else -级联。然而,这个问题几年前已经在 Perl 5.10.0 中得到了修复。这些天我看不出有什么理由不把上面的内容写成这样,这似乎比 (ab) 使用三元:

given ($n) {
    when (0)         { $t = "$_ ain't squawt"        }
    when (1)         { $t = "$_ is not a lot"        }
    when ($_ < 100)  { $t = "$_ is more than 1..."   }
    when ($_ < 1000) { $t = "$_ is in triple digits" }
    default          { $t = "Wow! $_ is thousands!"  }
}

或者,从 perl 5.13.1 开始,甚至是:

my $t = do {
    given ($n) {
        when (0)         { "$_ ain't squawt"        }
        when (1)         { "$_ is not a lot"        }
        when ($_ < 100)  { "$_ is more than 1..."   }
        when ($_ < 1000) { "$_ is in triple digits" }
        default          {  "Wow! $_ is thousands!" }
    }
};

更具 可读性这适用于所有 perl 版本:

my @cases = (
    [sub { $_ == 0 },   sub { "$_ ain't squawt"        }],
    [sub { $_ == 1 },   sub { "$_ is not a lot"        }],
    [sub { $_ < 100 },  sub { "$_ is more than 1..."   }],
    [sub { $_ < 1000 }, sub { "$_ is in triple digits" }],
    [sub { 1 },         sub { "Wow! $_ is thousands!"  }],
);

for my $case (@cases) {
    local $_ = $n;
    next unless $case->[0]->();
    $t = $case->[1]->();
    last;
}

虽然这避免了使用巨大的 if/elsif/else 级联,并且不需要最新 perls 的功能,但对于这个简单的示例来说可能不值得付出努力。然而,我非常认为这样的方法在很多条件下以及想要支持旧 perls 的限制下是有用的。

(另请注意,您的初始示例不处理 $n 小于零或根本不是数字。)

cjm 的注释:您也可以在 Perl 5 的所有版本中执行此操作:

my $t = do {
    if    ($n == 0)   { "$n ain't squawt"        }
    elsif ($n == 1)   { "$n is not a lot"        }
    elsif ($n < 100)  { "$n is more than 1..."   }
    elsif ($n < 1000) { "$n is in triple digits" }
    else              {  "Wow! $n is thousands!" }
};

I've seen this idiom used in perl. As the ternary operator ? : is, well.. an operator, it's documented in perlop, not perlsyn.

In my eyes, it is sort of idiomatic Perl, but the main purpose of this in Perl seems to avoid the lack of a proper switch statement while not writing huge if/else-cascades. However, this has been fixed years ago in perl 5.10.0. These days I can't see many reasons for not writing the above as this, which appears to be far more readable than (ab)using the ternary:

given ($n) {
    when (0)         { $t = "$_ ain't squawt"        }
    when (1)         { $t = "$_ is not a lot"        }
    when ($_ < 100)  { $t = "$_ is more than 1..."   }
    when ($_ < 1000) { $t = "$_ is in triple digits" }
    default          { $t = "Wow! $_ is thousands!"  }
}

or, as of perl 5.13.1, even as:

my $t = do {
    given ($n) {
        when (0)         { "$_ ain't squawt"        }
        when (1)         { "$_ is not a lot"        }
        when ($_ < 100)  { "$_ is more than 1..."   }
        when ($_ < 1000) { "$_ is in triple digits" }
        default          {  "Wow! $_ is thousands!" }
    }
};

Another alternative would be something like this, wich works on all perl versions:

my @cases = (
    [sub { $_ == 0 },   sub { "$_ ain't squawt"        }],
    [sub { $_ == 1 },   sub { "$_ is not a lot"        }],
    [sub { $_ < 100 },  sub { "$_ is more than 1..."   }],
    [sub { $_ < 1000 }, sub { "$_ is in triple digits" }],
    [sub { 1 },         sub { "Wow! $_ is thousands!"  }],
);

for my $case (@cases) {
    local $_ = $n;
    next unless $case->[0]->();
    $t = $case->[1]->();
    last;
}

While this avoids both using huge if/elsif/else-cascades, and doesn't need features of recent perls, it's probably not worth the effort for this simple example. However, I can very much see an approach like this being useful with lots of conditions and with the constraint of wanting to support old perls.

(Also note that your initial example doesn't handle $n being smaller than zero or not being a number at all.)

Note from cjm: You can also do this in all versions of Perl 5:

my $t = do {
    if    ($n == 0)   { "$n ain't squawt"        }
    elsif ($n == 1)   { "$n is not a lot"        }
    elsif ($n < 100)  { "$n is more than 1..."   }
    elsif ($n < 1000) { "$n is in triple digits" }
    else              {  "Wow! $n is thousands!" }
};
南风起 2024-10-03 23:33:18

条件运算符的布局很难阅读。
这更像是我记得 Perl 最佳实践 推荐的内容:

my $s = $n == 0   ? "$n ain't squawt"
      : $n == 1   ? "$n is not a lot"
      : $n < 100  ? "$n is more than 1..."
      : $n < 1000 ? "$n is in triple digits"
      :             "Wow! $n is thousands!";  # default...

有时最好也使用更紧凑的 if 表示法:

  if    ($n == 0)   { $t = "$n ain't squawt";        }
  elsif ($n == 1)   { $t = "$n is not a lot";        }
  elsif ($n < 100)  { $t = "$n is more than 1...";   }
  elsif ($n < 1000) { $t = "$n is in triple digits"; }
  else              { $t = "Wow! $n is thousands!" ; }  

这两种重新格式化都强调了代码各个部分的相似性,使其更易于阅读和理解。

The layout shown for the conditional operator is hard to read.
This is more like what I recall Perl Best Practices recommending:

my $s = $n == 0   ? "$n ain't squawt"
      : $n == 1   ? "$n is not a lot"
      : $n < 100  ? "$n is more than 1..."
      : $n < 1000 ? "$n is in triple digits"
      :             "Wow! $n is thousands!";  # default...

And there are times when it is better to use a more compact notation with the if notation, too:

  if    ($n == 0)   { $t = "$n ain't squawt";        }
  elsif ($n == 1)   { $t = "$n is not a lot";        }
  elsif ($n < 100)  { $t = "$n is more than 1...";   }
  elsif ($n < 1000) { $t = "$n is in triple digits"; }
  else              { $t = "Wow! $n is thousands!" ; }  

Both these reformattings emphasize the similarity of the various sections of the code, making it easier to read and understand.

诗笺 2024-10-03 23:33:18

我见过很多链式条件句,有时会使用它,但总是讨厌它。它很方便,但很难看,除非你极端地格式化它并简化间隙表达式。

一旦你遇到它们几次并意识到这是一个习语,它们就不那么难理解了。不过,正确的 switch 语句更容易理解。冒号错位以及以难以发现的方式搞乱一切的可能性也较小。

I've seen the chained conditionals quite a bit, used it sometimes, and hated it always. It's handy but ugly unless you go to extremes to format it and simplify the interstitial expressions.

They aren't so hard to understand once you've run across them a couple of times and realize it is an idiom. It's easier to understand a proper switch statement though. There's also less of a chance of misplacing a colon and messing everything up in a hard-to-spot way.

哥,最终变帅啦 2024-10-03 23:33:18

还有另一种方式!

my $t = sub {
    return "$n ain't squawt"        if $n == 0;
    return "$n is not a lot"        if $n == 1;
    return "$n is more than 1..."   if $n < 100;
    return "$n is in triple digits" if $n < 1000;
    return "Wow! $n is thousands!";
}->();

我在几篇博客文章中谈到了这一点:

/I3az/

And yet another way!

my $t = sub {
    return "$n ain't squawt"        if $n == 0;
    return "$n is not a lot"        if $n == 1;
    return "$n is more than 1..."   if $n < 100;
    return "$n is in triple digits" if $n < 1000;
    return "Wow! $n is thousands!";
}->();

I touch on this in a couple of blog posts I did:

/I3az/

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文