这似乎与已经提出的其他几个问题非常相关(例如,this one),但是我无法完全弄清楚该怎么做。也许替换功能是该工作的错误工具,这也将是一个完全可以接受的答案。我对Python的了解比对R的了解要多得多,我可以轻松地想到如何在Python中实现它,但是我不太了解如何在R中实现它。
问题:我试图在函数中修改对象,而不必返回它,但是我不需要传递值会对其进行修改,因为该值是对象中已包含的函数调用的结果。
更具体地说,我有一个列表(从技术上讲是s3类,但我认为实际上与该问题无关),其中包含一些与以processx::process$new()
开头的进程有关的东西。为了重现性,您可以运行以下玩具外壳脚本,以及获取我的res
对象的代码:
echo '
echo $1
sleep 1s
echo "naw 1"
sleep 1s
echo "naw 2"
sleep 1s
echo "naw 3"
sleep 1s
echo "naw 4"
sleep 1s
echo "naw 5"
echo "All done."
' > naw.sh
然后我的包装器是这样的:
run_sh <- function(.args, ...) {
p <- processx::process$new("sh", .args, ..., stdout = "|", stderr = "2>&1")
return(list(process = p, orig_args = .args, output = NULL))
}
res <- run_sh(c("naw.sh", "hello"))
res
应该看起来像
$process
PROCESS 'sh', running, pid 19882.
$output
NULL
$orig_args
[1] "naw.sh" "hello"
因此,这里的特定问题是process$new
所特有的,但我认为一般原则是相关的。我试图在此过程完成后收集该过程的所有输出,但是您只能调用一次process$new$read_all_output_lines()
(或它的同级函数),因为它第一次会从缓冲区返回结果,而随后的时间会什么也不返回。另外,我将调用其中的一堆,然后返回“检查它们”,因此我不能立即调用res$process$read_all_output_lines()
,因为那样一来它将等待过程完成,然后函数返回,这不是我想要的。
因此,我试图将该调用的输出存储在res$output
中,然后保留该调用并在后续调用中将其返回。如此... 我需要有一个功能,可以用res
来修改res$output <- res$process$read_all_output_lines()
。
这是我根据this这样的指导尝试过的方法,但是没有用。
get_output <- function(.res) {
# check if process is still alive (as of now, can only get output from finished process)
if (.res$process$is_alive()) {
warning(paste0("Process ", .res$process$get_pid(), " is still running. You cannot read the output until it is finished."))
invisible()
} else {
# if output has not been read from buffer, read it
if (is.null(.res$output)) {
output <- .res$process$read_all_output_lines()
update_output(.res) <- output
}
# return output
return(.res$output)
}
}
`update_output<-` <- function(.res, ..., value) {
.res$output <- value
.res
}
第一次调用get_output(res)
有效,但是不将输出存储在res$output
中供以后访问,因此后续调用均不返回任何内容。
我也尝试过这样的事情:
`get_output2<-` <- function(.res, value) {
# check if process is still alive (as of now, can only get output from finished process)
if (.res$process$is_alive()) {
warning(paste0("Process ", .res$process$get_pid(), " is still running. You cannot read the output until it is finished."))
.res
} else {
# if output has not been read from buffer, read it
if (is.null(.res$output)) {
output <- .res$process$read_all_output_lines()
update_output(.res) <- output
}
# return output
print(value)
.res
}
}
这只是丢掉value
,但这感觉很愚蠢,因为您必须使用我讨厌的get_output(res) <- "fake"
这样的作业来调用它。
很明显,我也可以返回修改后的res
对象,但是我不喜欢这样做,因为那样的话,用户必须知道要res <- get_output(res)
,并且如果他们忘记这样做了(第一次),则输出将丢失给以太坊,无法恢复。不好。
非常感谢您的帮助!
答案 0 :(得分:1)
我可能在这里丢失了一些东西,但是为什么不创建对象后就只写输出,以便在函数第一次返回时就在那儿?
run_sh <- function(.args, ...)
{
p <- processx::process$new("sh", .args, ..., stdout = "|", stderr = "2>&1")
return(list(process = p, orig_args = .args, output = p$read_all_output_lines()))
}
所以现在就可以了
res <- run_sh(c("naw.sh", "hello"))
你得到
res
#> $`process`
#> PROCESS 'sh', finished.
#>
#> $orig_args
#> [1] "naw.sh" "hello"
#>
#> $output
#> [1] "hello"
#> [2] "naw.sh: line 2: sleep: command not found"
#> [3] "naw 1"
#> [4] "naw.sh: line 4: sleep: command not found"
#> [5] "naw 2"
#> [6] "naw.sh: line 6: sleep: command not found"
#> [7] "naw 3"
#> [8] "naw.sh: line 8: sleep: command not found"
#> [9] "naw 4"
#> [10] "naw.sh: line 10: sleep: command not found"
#> [11] "naw 5"
#> [12] "All done."
答案 1 :(得分:1)
从OP获得更多信息之后,似乎需要的是一种在调用函数的环境中写入现有变量的方法。这可以通过非标准评估来完成:
check_result <- function(process_list)
{
# Capture the name of the passed object as a string
list_name <- deparse(substitute(process_list))
# Check the object exists in the calling environment
if(!exists(list_name, envir = parent.frame()))
stop("Object '", list_name, "' not found")
# Create a local copy of the passed object in function scope
copy_of_process_list <- get(list_name, envir = parent.frame())
# If the process has completed, write its output to the copy
# and assign the copy to the name of the object in the calling frame
if(length(copy_of_process_list$process$get_exit_status()) > 0)
{
copy_of_process_list$output <- copy_of_process_list$process$read_all_output_lines()
assign(list_name, copy_of_process_list, envir = parent.frame())
}
print(copy_of_process_list)
}
如果该过程完成,它将更新res
;否则,它会孤单。无论哪种情况,它都会打印出当前内容。如果这是面向客户的代码,则需要对传入的对象进行进一步的类型检查逻辑。
所以我可以做
res <- run_sh(c("naw.sh", "hello"))
并检查res
我的内容:
res
#> $`process`
#> PROCESS 'sh', running, pid 1112.
#>
#> $orig_args
#> [1] "naw.sh" "hello"
#>
#> $output
#> NULL
如果我立即运行:
check_result(res)
#> $`process`
#> PROCESS 'sh', running, pid 1112.
#>
#> $orig_args
#> [1] "naw.sh" "hello"
#>
#> $output
#> NULL
我们可以看到该过程尚未完成。但是,如果我等待几秒钟并再次致电check_result
,则会得到:
check_result(res)
#> $`process`
#> PROCESS 'sh', finished.
#>
#> $orig_args
#> [1] "naw.sh" "hello"
#>
#> $output
#> [1] "hello" "naw 1" "naw 2" "naw 3" "naw 4" "naw 5"
#> [7] "All done."
并且没有显式写入res,它已通过以下功能进行了更新:
res
#> $`process`
#> PROCESS 'sh', finished.
#>
#> $orig_args
#> [1] "naw.sh" "hello"
#>
#> $output
#> [1] "hello" "naw 1" "naw 2" "naw 3" "naw 4" "naw 5"
#> [7] "All done."