Giu*_*ame 1 r function return-value
我正在制作一个简单的 R 函数,以相同的方式一致地将文件写入磁盘,但在不同的文件夹中:
library(magrittr)
main_path <- here::here()
write_to_disk <- function(data, folder, name){
data %>%
vroom::vroom_write(
file.path(main_path, folder, paste0(name, ".tsv"))
)
}
Run Code Online (Sandbox Code Playgroud)
我知道我不一定需要在 R 函数中返回任何内容,但如果需要,return()这里合适的语句是什么?
多谢
老实说,这很容易产生意见和背景,但有一些想法:
返回原始数据。大多数 tidyverse 动词函数和许多其他包(以及一些基于 R 的包)都传达了这种精神。如果您使用%>%或|>管道,这样做可以在函数之后处理数据,这可能非常方便。
write_to_disk <- function(data, folder, name){
vroom::vroom_write(
data,
file.path(folder, paste0(name, ".tsv"))
)
data
}
Run Code Online (Sandbox Code Playgroud)
(您的函数已经隐式执行此操作,因为调用的vroom::vroom_write是函数体中的最后一个表达式。)
返回文件写入调用的输出。坦率地说,我不太喜欢这个,因为如果您更改了包装器使用的函数,那么函数的返回值很可能会发生变化。我不知道你的包装函数的生命周期预期,但想象一下如果你选择从vroom::vroom另一个函数切换;vroom返回基于 的数据子集col_select,也许较新的函数将返回整个数据,这可能会打破下游处理的假设。
write_to_disk <- function(data, folder, name){
out <- vroom::vroom_write(
data,
file.path(folder, paste0(name, ".tsv"))
)
out
}
Run Code Online (Sandbox Code Playgroud)
注意:我明确选择捕获out并返回它,以防您在vroom::vroom_write和后续之间添加代码out。您的原始函数保持不变实际上是在做同样的事情,但是如果您选择在 post- 后执行任何操作vroom_write,那么这个额外的步骤将是必要的。
否则,您的函数已经隐式执行此操作,因为vroom::vroom_write返回了数据。
返回文件名。仅当文件名不一定是先验已知时这才有用。例如,如果您的包装器注意不要覆盖同名文件,它可能会添加一个计数器(预扩展),以便永远不会发生覆盖。在这种情况下,调用环境不知道所选文件名是什么,因此它(有时)有价值。
write_to_disk <- function(data, folder, name){
# file numbering
re <- paste0("^", name, "_?([0-9]+)?\\.tsv$")
existfiles <- list.files(folder, pattern = re, full.names = TRUE)
nextnum <- max(0L, suppressWarnings(as.integer(gsub(re, "\\1", basename(existfiles)))), na.rm = TRUE)
if (nextnum > 0) {
name <- sprintf("%s_%03i", name, nextnum + 1L)
}
filename <- file.path(folder, paste0(name, ".tsv"))
vroom::vroom_write(
data,
filename
)
filename
}
Run Code Online (Sandbox Code Playgroud)
(“文件编号”代码仅作为我认为返回文件名可能有意义的示例。)
返回写入函数成功。这可能需要使用tryor tryCatch(或任何 tidyverse 等效项)、捕获错误并做出相应反应。
write_to_disk <- function(data, folder, name){
res <- tryCatch(
vroom::vroom_write(
data,
file.path(folder, paste0(name, ".tsv"))
),
error = function(e) e
)
out <- !inherits(res, "error")
if (!out) {
attr(out, "error") <- conditionMessage(res)
}
out
}
Run Code Online (Sandbox Code Playgroud)
什么也不返回。这当然是最简单的。您需要显式地执行此操作,以免无意中从文件写入函数返回返回值。
write_to_disk <- function(data, folder, name){
vroom::vroom_write(
data,
file.path(folder, paste0(name, ".tsv"))
)
NULL
}
Run Code Online (Sandbox Code Playgroud)
笔记:
您的使用与函数式编程main_path相反,因为根据其直接范围之外的内容的存在,在给定相同输入的情况下,函数的行为会有所不同。我认为最好通过,write_to_dist(x, file.path(main_path, folder), "somename")因为main_path是在该环境中定义的(而不是在函数内),并且您的函数足够通用,不需要正确定义该变量。
我已经更新了上面的所有代码以反映这一良好实践。如果您强烈反对这一点,请随时添加回main_path您喜欢的位置。
对于以上任何一个以不可见的方式返回可能很有用,这样(例如)保存一个大的data.frame而不捕获其返回值不会使控制台充满数据。这很容易做到,invisible(data)并且不会改变任何返回值(除了默认情况下它不打印在控制台上)。
仅供参考:康拉德和我在评论中反复讨论这是否return(.)是一个好主意。我并不反对大多数主张,并且认为,不管它与风格和观点有关,还是与其他因素有关。无论如何,由于我的大部分论点在上述所有代码中都没有实际意义,因此为了简洁起见,我将其删除。 return