【问题标题】:Under what circumstances is "unless" different from "if not"?在什么情况下“除非”与“如果不是”不同?
【发布时间】:2017-07-17 22:39:34
【问题描述】:

我以前认为unlessif not 是完全等价的,但this Q&A 让我意识到它们在列表上下文中会产生不同的输出:

use strict;
use warnings;
use Data::Printer;
use feature 'say';

my %subs = (
              unless => sub {
                            my ( $value ) = @_ ;

                            return "$value is a multiple of 3"
                              unless $value % 3 ;
                        },
              if_not => sub {
                            my ( $value ) = @_ ;

                            return "$value is a multiple of 3"
                              if not $value % 3 ;
                        },
           );

for my $name ( keys %subs ) {

    my $sub = $subs{$name};
    say $name;
    my @results = map { $sub->($_) } 1 .. 10;
    p @results;
}

输出

if_not
[
    [0] "",
    [1] "",
    [2] "3 is a multiple of 3",
    [3] "",
    [4] "",
    [5] "6 is a multiple of 3",
    [6] "",
    [7] "",
    [8] "9 is a multiple of 3",
    [9] ""
]
unless
[
    [0] 1,
    [1] 2,
    [2] "3 is a multiple of 3",
    [3] 1,
    [4] 2,
    [5] "6 is a multiple of 3",
    [6] 1,
    [7] 2,
    [8] "9 is a multiple of 3",
    [9] 1
]

上面的代码 sn-p 表明 unlessif not 导致子例程看到最后一个求值表达式的不同值。

这似乎是因为if not 认为notEXPR 的一部分,而unless 不认为它是EXPR 的一部分。

问题

是否还有其他代码使用示例,而这两者并不完全是同义词?

【问题讨论】:

  • "if not 认为notEXPR 的一部分,而unless 不认为它是EXPR 的一部分"没有多大意义。 not运算符,而ifunless 是流控制语言单词。在您的 unless 情况下,根本没有 not,因此“将 not 视为表达式的一部分”不适用。如果您也尝试过unless not ...,那么您会发现not 是表达式的始终 的一部分,就像! 一样。 ifunless 只是根据表达式的真相做出相反的行为。
  • 如果“$value % 3”为假,则可以找到“return”EXPR。如果 "$value % 3" 为真,则找不到 "return" EXPR,"if_not" 将返回 "not $value % 3",而 "unless" 将返回 "$value % 3"。
  • 我认为“问题”之前的最后一个陈述可能具有误导性。这里的重点是return if EXPR; 似乎有if(本身)是最后处理的东西(所以首先评估EXPR,然后评估其结果的if),而return unless EXPR; 似乎有EXPR 处理最后的。 (这与not 无关。也与return-ing 无关。)如果if EXPRunless EXPR 导致不同的评估顺序,我认为这很重要。
  • @zdim:ifunless 都不返回值,因此无法“评估”。在没有显式return 语句的情况下返回的值为EXPR,即$value % 3not $value % 3。我不明白你为什么要说“这不是关于not。这也不是关于return-ing。” 很明显是。在这两种情况下,都会评估EXPR,然后可能会执行return,具体取决于EXPR 的值。
  • @Borodin 同意——我认为你答案中的前两段清楚地表明了这一点。但是,我不同意“需要关闭”的问题。我认为这是一个很好的问题。基本构造行为的感知差异将是显着的,应该通过各种方式提出。它实际上如何表述并不​​重要——对于类似的事情,可以要求“其他示例”。 unlessif 之间在操作中解释的“非差异”也很重要,因为 unless 通常被视为 if not

标签: perl


【解决方案1】:

不同之处在于,对于您的 unless 案例,最后评估的表达式是 $value % 3 而对于您的 if not 案例,最后评估的表达式是 not $value % 3

not 表现为一个简单的运算符,而 ifunless 是像 whilefor 这样的语言结构,它们不涉及表达式的值

在任何情况下,依赖子例程的返回值都是不好的做法,除非每个路径都有一个return,或者它以表达式结尾。 perldoc perlsub 这么说

如果没有找到return,并且最后一条语句是表达式,则返回其值。如果最后一条语句是循环控制结构,例如 foreachwhile,则返回值未指定。

所以仅仅因为你认为你知道没有return 的子例程也会计算什么,它实际上并不可靠,除非你结束你的子例程用一个表达式,并且很可能在以后的版本中改变Perl。

只需为您的其他情况写一个return,您的代码就会立即变得更清晰。或者您可以使用 条件表达式 指定要为 both 条件返回的值

cond_exp => sub {
    my ( $value ) = @_;

    $value % 3 ? "" : "$value is a multiple of 3";
}

【讨论】:

  • 我认为这个问题已经说得很清楚了,但我并不是要解释为什么两者之间的行为会有所不同;我想知道是否还有其他示例 unlessif not 行为不同。我们已经习惯了看到unless 可以重写为if not,所以上面示例中的差异一开始有点令人惊讶。是否还有其他使用 unlessif not 导致不同结果的示例?
  • @Zaid:直接回答你的问题几乎是不可能的。任何其他这样的情况都会像这个一样晦涩难懂,仅仅因为我无法立即想象任何其他情况并不意味着没有。您的问题来自一种误解,即 unlessif not 的等价性并不意味着您可以在不改变代码语义的情况下将一个字符串替换为另一个。我希望,如果我解释了差异的来源,您将能够自己发现并避免类似情况。
  • @Zaid:人们说unless 等同于if not 的意思是unlessif 可以交换,只要它们的布尔控制表达式也被否定。您不能通过仅将not 放在布尔表达式前面来可靠地反转布尔表达式的含义,并且必须对规则应用一些智能。请注意,例如,普通的unless(不是语句修饰符)在其表达式周围有括号。但这并不意味着unless ( $value ) { ... } 可以替换为if not ( $value ) { ... }。那甚至不会编译。
  • @Zaid 我相信第一段(连同第二段)解释了我们谈话中导致这一点的错误。 (因为我提出了“unless is if not”错误,所以我认为这个错误是错误的。)我们应该比较 unlessif(不是 not!)然后有没有混淆:unless (EXPR) 返回余数,而 if (!EXPR) 不正确。所以行为是一致的。前两段清楚地表明了这一点。我仍然认为这是一个很好且有价值的问题,这篇文章回答了它。
  • 同意,这正是您评论中的最后一句话漏掉的地方。当我终于更好地测试它时,我想出了return unless $_[0] > 5return if $_[0] <= 5(在两个潜艇中),随后的后续行动点亮了灯泡。我想我在某个地方想到了unlessif not——它不是。我确实认为值得在问答中澄清。
猜你喜欢
  • 1970-01-01
  • 2011-03-27
  • 2011-09-17
  • 1970-01-01
  • 1970-01-01
  • 2014-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多