等同于 Mathematica 的 Which

Equivalent of Mathematica's Which

MathematicaWhich 函数是广义的 If:

Which[test_1, value_1, test_2, value_2, …]

evaluates each of the test_i in turn, returning the value of the value_i corresponding to the first one that yields True.

它只不过是一种从嵌套的简单 if-else 测试的长序列中删除多余语法的简便方法。

R 有等效的功能吗?


顺便说一句,我知道我总是可以做类似

的事情
if (test_1) value_1 else if (test_2) value_2 else ... value_n else default

或者,等价地,

if (test_1) value_1 else
if (test_2) value_2 else
            ...
if (test_n) value_n else
            default

...但是,正如我已经提到的,与 Which 相比,嵌套的 if-else 语句引入了很多多余的语法。

此外,我知道

ifelse(t_1, v_1, ifelse(t_2, v_2, ..., ifelse(t_n, v_n, default)...))

...但结果对测试的形状很敏感,因此它并不严格等同于嵌套 if-else 语句。

最后,R 的 switch 语句与我正在寻找的相似,因为它封装了对一系列测试的调度,但这并不完全相同。在

switch(expr,
       case_1 = value_1,
       case_2 = value_2,
       ...
       case_n = value_n,
       default)

...测试都是 exprcase_i 的相等比较,而在 Which 等中,测试是任意布尔表达式。

根据 Mathematica 帮助,

Which[test1,value1,test2,value2,…] evaluates each of the testi in turn, returning the value of the valuei corresponding to the first one that yields True.

我们可以在 R 中执行此操作,即获取与计算结果为真的第一个表达式的位置对应的值,如下所示:

1.计算所有表达式的简单版本:

values = c("value1", "value2", "value3", "value4", "value5", "value6", "value7")
expressions = c(1==2, 1==3, 1==1, 1==4, T==F, F==T, T==T)
values[which.max(expressions)]
# [1] "value3"

虽然,如果 none 的表达式为真,which.max 将 return 第一个为假,所以我们也应该检查这个

if (any(expressions))  values[which.max(expressions)] else NA

2。 'short circuits'

的版本

但是,上面的行为与 Mathematica 有一个区别:which 在 Mathematica 短路中 - 即它只计算找到第一个 TRUE 所需的尽可能多的表达式。如果表达式的计算成本很高或存在速度瓶颈,那么我们可能还想在 R 中复制这种行为。我们可以使用 Position 来实现这一点,它会短路,并结合 eval(parse) 来确保我们在我们准备好测试它们之前不要评估表达式

values = c("value1", "value2", "value3", "value4", "value5", "value6", "value7")
expressions = c("1==2", "1==3", "1==1", "1==4", "T==F", "F==T", "T==F")

values[Position(function(text) eval(parse(text=text)), expressions, T)]

您可以编写自己的函数,用作此类控制结构。以下是基于 match.call 支持惰性求值的事实。 (参见 this 接受的答案):

which.val <- function(...){
  clauses <- match.call(expand.dots = FALSE)$`...`
  n <- length(clauses)
  for(i in seq(1,n,2)){
    condition = eval(clauses[[i]], envir = parent.frame())
    if(condition) return(eval(clauses[[i+1]], envir = parent.frame()))
  }
}

出于测试目的:

test <- function(a,b){
  print(b)
  a == b
}

可以通过side effect看实际评价的是什么。

例如:

> x <- 3
> which.val(test(x,1),10,test(x,2),20,test(x,3),30,test(x,4),40)
[1] 1
[1] 2
[1] 3
[1] 30

请注意 test(x,4) 从未被计算过。