---
title: which() 咩？
date: '2017-04-19'
slug: which
---

R 里面有个 `which()` 函数，我感觉很多人一旦学了就忘不了，总是想用。它可以把逻辑向量转化为整数向量，这些整数表示哪些位置上的值是 `TRUE`，如 `which(c(F, T, F))` 就会返回 2。我见过好些人在用下标索引的时候硬生生把逻辑向量用 `which()` 转一下，比如 `x[which(y)]`（`y` 是逻辑向量），这个 `which()` 的调用完全没必要，因为 R 有三种索引方式：整数值、逻辑值、字符（名字）。可能一般人还是习惯整数索引吧。

作为强迫症患者，我尽量不做无谓的强迫，尽管这个有点难。比如要不要提出来 `which()` 是不必要的，其实不能完全根据自己的喜好来判断，而是要客观测量它究竟有多糟糕。糟不糟糕，主要准则应该是速度，比如用了 `which()` 会导致代码显著变慢吗？理论上多调用一个函数肯定会更慢，但我用三秒钟的时间想了一下，万一 `which()` 慢，但整数索引比逻辑索引更快，那时间有没有可能追回来？

空谈误国，放码过来。前方测速区：

```r
n = 100000
x = runif(n)
i = logical(n)
i[sample(n, n/4)] = TRUE

microbenchmark::microbenchmark(x[i], x[which(i)], times = 1000)
```

跑了一下，貌似没那么糟糕。

```
Unit: microseconds
        expr   min    lq  mean median    uq   max neval
        x[i] 362.4 488.6 719.6  577.2 669.7  6723  1000
 x[which(i)] 438.4 581.3 900.7  670.2 795.8 82968  1000
```

我还有个类似的洁癖，就是不愿意看到 `if (x == TRUE)` 这种写法，`if (x)` 就足够。去年在芝加哥参加 JSM 的时候我已经[吐槽过一次](https://twitter.com/xieyihui/status/759863505357012996)。不过呢，把 `TRUE` 写出来倒也不是完全没有好处：它的潜在好处是快速扫描代码的时候很容易知道这里有个逻辑值，因为这四个大写字母比较耀眼。
