【问题标题】:Assigning values from if-else blocks (how does it work?)从 if-else 块分配值(它是如何工作的?)
【发布时间】:2009-12-02 15:27:59
【问题描述】:

recently discovered,您可以使用 if-else 块有条件地分配一个值。

y <- if(condition) 1 else 2

我意识到这个用例是有限的:如果你有矢量化代码,你会使用 ifelse 函数来代替。有一个性能优势:在我的机器上,if-else 在标量情况下的运行速度比 ifelse 快大约 35 倍(尽管您需要调用它数百万次才能注意到很大的不同)。

让我烦恼的是,我不知道为什么这段代码有效——我很惊讶它不只是抛出一个错误。

另一个例子表明,如果块的行为类似于函数——它们会自动返回块中的最后一个值(尽管您不能在其中使用 return 语句)。

y <- if(TRUE) 
{
   y <- 3
   4
}    # y is 4

基于此,我猜测可能是在你进入 if 块时创建了另一个环境,但似乎并非如此。

if(TRUE) sys.frames()    # NULL

谁能告诉我幕后发生了什么?

【问题讨论】:

    标签: r if-statement


    【解决方案1】:

    让我专注于

    困扰我的是我做不到 弄清楚为什么这段代码有效——我是 惊讶于它不只是抛出一个 错误。

    你为什么这么认为?发生的事情是我们有

    1. y 被分配了一个表达式
    2. 那个表达式是一个 if if() ...
    3. 在测试中导致TRUEFALSE
    4. 导致进入两个分支之一
    5. 导致各自的代码被评估
    6. 导致其最终值为右侧的值
    7. 导致这个值被分配给y

    对我来说似乎是合乎逻辑的。

    【讨论】:

    • 也许我已经过度(或不足)思考这个问题,我们可以把它归结为非常聪明的解析。作为记录,当你在 MATLAB 中尝试这样的事情时,你会得到一个错误:Illegal use of reserved keyword "if".
    • R 不是 MATLAB,但它没有理由必须遵循 MATLAB 的规则。这里将 if 块中评估的最后一条语句的值分配给 y。该语句是标量 4。
    • Python 也会中断,C# 也是如此(尽管您可以使用 ? 运算符,正如 Ken 指出的那样)。我认为关键在于语句的右侧被视为表达式并在之前进行赋值。
    【解决方案2】:

    if(....) ... else ... 等控制结构等效于以相关(惰性求值)子表达式作为参数的函数调用:

    `if`(TRUE, "yes", "no")
    

    基本上,if 是一个特殊原语函数:

    R> sapply(ls("package:base", all=TRUE), function(x) is.primitive(get(x)))["if"]
      if 
    TRUE 
    R> sapply(ls("package:base", all=TRUE), function(x) typeof(get(x)))["if"]
           if 
    "special" 
    

    R Language Definition manual 中的相关部分(第 3.2.1 节)说:

    因为 if/else 语句与其他语句相同,您可以为它们赋值。下面的两个例子是等价的。

    R> if( any(x <= 0) ) y <- log(1+x) else y <- log(x)
    R> y <- if( any(x <= 0) ) log(1+x) else log(x)
    

    【讨论】:

    • @rcs:感谢您的参考。我可能应该通过手册来更清楚地了解 R 正在做什么。
    • 我还可以推荐数据分析软件中的第 13 章(R 工作原理) - 使用 R 编程(John Chambers)
    【解决方案3】:

    关于您问题的第二部分 - {} 用于对表达式进行分组,它的工作方式与函数定义不同。正如您在help("Paren") 中看到的那样:

    对于‘{’,计算最后一个表达式的结果。

    但是所有的表达式都是在当前环境中求值的:

    y <- if (TRUE) {
        print(paste("Current frame:",sys.nframe()))
        y <- 3
        z <- 5
        4
    }
    # [1] "Current frame: 0"
    # result:
    y # [1] 4
    z # [1] 5
    
    # compare to use function:
    v <- if(TRUE) (function(){      
        print(paste("Current frame:",sys.nframe()))
        v <- 3
        w <- 5
        4
    })()
    # [1] "Current frame: 1"
    v # [1] 4
    w # Error: object 'w' not found
    

    结论是块的行为不像函数

    编辑: 如果你想使用块作为函数,你可以使用local:

    a <- if (TRUE) local({
        print(paste("Current frame:",sys.nframe()))
        a <- 3
        b <- 5
        4
    })
    # [1] "Current frame: 6"
    a # [1] 4
    b # Error: object 'b' not found
    

    那么你也可以使用return

    a <- if (TRUE) local({
        print(paste("Current frame:",sys.nframe()))
        a <- 3
        b <- 5
        return(7)
        4
    })
    # [1] "Current frame: 6"
    a # [1] 7
    b # Error: object 'b' not found
    

    【讨论】:

    • @Marek:一个有趣的分析。我没有考虑过 ( 和 { 将是函数。
    【解决方案4】:

    这不是很深 - 许多语言为此使用“val = x ? y : z”结构。在 R 中,它只是折叠成一个 if/else 结构,所以你改写为“val = if(x) y else z”。

    【讨论】:

    • 好点儿 –我想它的解析方式或多或少与三元运算符相同。
    猜你喜欢
    • 2014-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-12
    • 1970-01-01
    • 2019-10-13
    • 1970-01-01
    相关资源
    最近更新 更多