从lapply或使用print语句从函数调用时,kable的意外行为

seb*_*eau 8 r lapply rstudio knitr

我正在尝试理解使用knitr包编织HTML时在kable函数中的两个意外行为(在Ubuntu 14.04上的RStudio 0.98.977中):

  1. 当从lapply内部进行两次kable调用时,只有第一次调用在最终的HTML中产生漂亮的显示.
  2. 当在一个也使用print语句的函数中进行两次kable调用时,只有最后一次调用才会在最终的HTML中产生漂亮的显示.

示例代码如下:

Load library:

```{r init}
library("knitr")
```

Define dataframe:

```{r define_dataframe}
df <- data.frame(letters=c("a", "b", "c"), numbers=c(1, 2, 3))
rownames(df) <- c("x", "y", "z")
```

### Example 1: pretty display with simple call

The dataframe is displayed nicely twice when knitting HTML with the following code:

```{r pretty_display1, results="asis"}
kable(df)
kable(df)
```

### Example 2: unexpected display with lapply

The dataframe is displayed nicely only the first time when knitting HTML with the following code:

```{r unexpected_display1, results="asis"}
lst <- list(df, df)
lapply(lst, kable)
```

### Example 3: pretty display with function

The dataframe is displayed nicely twice when knitting HTML with the following code:

```{r pretty_display2, results="asis"}
foo1 <- function (df) {
  kable(df)
}
foo2 <- function (df) {
  foo1(df)
  foo1(df)
}
foo2(df)
```

### Example 4: unexpected display with function containing print statements

The dataframe is displayed nicely only the second time when knitting HTML with the following code:

```{r unexpected_display2, results="asis"}
foo1 <- function (df) {
  kable(df)
}
foo2 <- function (df) {
  print("first display")
  foo1(df)
  print("second display")
  foo1(df)
}
foo2(df)
```
Run Code Online (Sandbox Code Playgroud)

你对这些奇怪的行为以及如何规避它们有解释吗?

nog*_*pes 9

输出kable是副作用; 您可以将输出的值存储在变量中,但只是运行kable会将某些内容输出到控制台.当你运行kable(df)两次时,这不是问题,你没有存储任何东西,并且该函数将输出转储到控制台两次.

但是,在运行时lapply(lst, kable),该函数会将输出转储到控制台,然后显示列表的值.尝试在您的控制台中运行它:

lst <- list(df, df)
lapply(lst, kable)
Run Code Online (Sandbox Code Playgroud)

你应该得到这个:

|   |letters | numbers|
|:--|:-------|-------:|
|x  |a       |       1|
|y  |b       |       2|
|z  |c       |       3|


|   |letters | numbers|
|:--|:-------|-------:|
|x  |a       |       1|
|y  |b       |       2|
|z  |c       |       3|
[[1]]
[1] "|   |letters | numbers|" "|:--|:-------|-------:|"
[3] "|x  |a       |       1|" "|y  |b       |       2|"
[5] "|z  |c       |       3|"

[[2]]
[1] "|   |letters | numbers|" "|:--|:-------|-------:|"
[3] "|x  |a       |       1|" "|y  |b       |       2|"
[5] "|z  |c       |       3|"
Run Code Online (Sandbox Code Playgroud)

注意如何输出正确的降价,然后显示您创建的列表的实际值.这就是产生不良输出的原因.

功能范例在副作用方面效果不佳,因此您有两种选择.您可以kable通过设置output参数来存储结果FALSE,或者您可以使用a for来浏览您的结果list,或者您可以阻止显示结果列表.这里有一些可行的例子.

```{r nograpes1, results="asis"}
lst <- list(df, df)
for(x in lst) kable(x) # Don't create a list, just run the function over each element
```

```{r nograpes2, results="asis"}
lst <- list(df, df)
invisible(lapply(lst, kable)) # prevent the displaying of the result list.
```

```{r nograpes3, results="asis"}
lst <- list(df, df)
l <- lapply(lst, kable) # Store the list and do nothing with it.
```
Run Code Online (Sandbox Code Playgroud)

在我看来,这是一个很好的例子,for 应该在R中使用何时,因为它最清楚地表达了你想如何使用基于副作用的函数.