Skip to Content

Yet another dark magic in R, empty argument

knitr::opts_chunk$set(error = TRUE, collapse = TRUE)
# install needed R packages
remotes::update_packages(c('magrittr', 'testthat', 'tibble'), upgrade = TRUE)
remotes::install_github(c('krlmlr/deparse'))

1 Beginning

在用 testthat 的时候发现了 dput() 真的很好用 1,就想找一下有没有把 tibble 转化成 tribble(...) 的函数。

结果 krlmlr 大人已经 实现 了:

(df <- tibble::tibble(x = 1:2, y = 2:1))
## # A tibble: 2 x 2
##       x     y
##   <int> <int>
## 1     1     2
## 2     2     1

deparse::deparsec(df, as_tribble = TRUE)
## Error in loadNamespace(name): there is no package called 'deparse'

2 Development

继续逛了一下,看到有 好事者 还是不满意,觉得

tribble(
  ~mpg, ~cyl, ~disp, ~hp, ~drat, ~wt,   ~qsec, ~vs, ~am, ~gear, ~carb,
  21,   6,    160,   110, 3.9,   2.62,  16.46, 0,   1,   4,     4,    
  21,   6,    160,   110, 3.9,   2.875, 17.02, 0,   1,   4,     4    
)

应该变成

tibble::tribble(
    ~mpg, ~cyl, ~disp, ~hp, ~drat,   ~wt, ~qsec, ~vs, ~am, ~gear, ~carb
  ,   21,    6,   160, 110,   3.9,  2.62, 16.46,   0,   1,     4,     4
  ,   21,    6,   160, 110,   3.9, 2.875, 17.02,   0,   1,     4,     4
)

他说的倒很有道理,后者使得调换不同的行变得很方便(前者把第1、2 行互换的话,需要给新的第1行加个,,再从新的第2行删个,)。但是 krlmlr 大人却提出:

tribble(
  ~a, ~b,
   1,  2,
)

3 Climax

这就很有意思了,不会造成语法错误吗?

Thus I have a try myself, and find that empty argument means using parameter’s default value:

foobar <- function(a=1,b=2) {
    print(a)
    print(b)
}

foobar(3,)
## [1] 3
## [1] 2
foobar(,4)
## [1] 1
## [1] 4
foobar(3,4,)
## Error in foobar(3, 4, ): unused argument (alist())

Then I recall that I shouldn’t feel strange for this syntax:

mtcars[1, ]
mtcars[ , ]

4 Epilogue

Understanding what happens, I start to think maybe I can write read_csv('', , 'cc') instead of read_csv('', T, 'cc').

No, absolutely not!

If I really don’t want to type the T, I’d better write read_csv('', col_types = 'cc').

Type 10 more character now, save a lot of times in the future.


  1. 例如你要检查 x 是不是期望的值,dput() 就可以告诉你什么样的 R 代码可以构造出期望的值:

    x <- list()
    x$a = 1:3
    x$b = c('a', 'b', 'c')
    
    dput(x)
    ## list(a = 1:3, b = c("a", "b", "c"))
    
    testthat::expect_identical(x, list(a = 1:3, b = c("a", "b", "c")))
    testthat::expect_identical(x[1], list(a = 1:3, b = c("a", "b", "c")))
    ## Error: x[1] not identical to list(a = 1:3, b = c("a", "b", "c")).
    ## Length mismatch: comparison on first 1 components
    ↩︎