R 中不接受输入的替换函数
Replacement functions in R that don't take input
这似乎与已提出的其他几个问题非常相关(例如 this one),但我不太清楚如何做我想做的事情。也许替换功能是这项工作的错误工具,这也是一个完全可以接受的答案。我对 Python 比 R 更熟悉,我可以很容易地想到我想在 Python 中如何做,但我不太清楚如何在 R 中处理它。
问题: 我正在尝试修改函数内的对象,而不必 return 它,但我 不这样做不需要传入修改它的值,因为这个值是对象中已经包含的函数调用的结果。
更具体地说,我有一个列表(从技术上讲它是一个 s3 class,但我认为这实际上与这个问题无关)包含一些与以 [=15= 开始的进程相关的内容] 称呼。为了可重复性,这里有一个玩具 shell 脚本,你可以 运行,以及获取我的 res
对象的代码:
echo '
echo
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()
(或者它的同级函数)一次,因为第一次它将 return 来自缓冲区的结果和随后的时间 return 什么都没有。此外,我将调用其中的一些然后返回到 "check on them" 所以我不能立即调用 res$process$read_all_output_lines()
因为它会在函数 [= 之前等待进程完成72=]s,不是我想要的。
所以我试图将该调用的输出存储在 res$output
中,然后在后续调用中保留它和 return。 Soooo... 我需要一个函数来修改 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
中以供稍后访问,因此后续调用 return 没什么。
我也试过这样的:
`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"
之类的赋值来调用它。
显然我也可以只 return 修改后的 res
对象,但我不喜欢那样,因为这样用户必须知道要做什么 res <- get_output(res)
并且如果他们忘记了这样做(第一次)然后输出会丢失到以太中并且永远无法恢复。不好。
非常感谢任何帮助!
我可能在这里遗漏了一些东西,但是你为什么不在创建对象后只写输出,以便它在函数第一次出现时就在那里returns?
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."
从 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."
这似乎与已提出的其他几个问题非常相关(例如 this one),但我不太清楚如何做我想做的事情。也许替换功能是这项工作的错误工具,这也是一个完全可以接受的答案。我对 Python 比 R 更熟悉,我可以很容易地想到我想在 Python 中如何做,但我不太清楚如何在 R 中处理它。
问题: 我正在尝试修改函数内的对象,而不必 return 它,但我 不这样做不需要传入修改它的值,因为这个值是对象中已经包含的函数调用的结果。
更具体地说,我有一个列表(从技术上讲它是一个 s3 class,但我认为这实际上与这个问题无关)包含一些与以 [=15= 开始的进程相关的内容] 称呼。为了可重复性,这里有一个玩具 shell 脚本,你可以 运行,以及获取我的 res
对象的代码:
echo '
echo
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()
(或者它的同级函数)一次,因为第一次它将 return 来自缓冲区的结果和随后的时间 return 什么都没有。此外,我将调用其中的一些然后返回到 "check on them" 所以我不能立即调用 res$process$read_all_output_lines()
因为它会在函数 [= 之前等待进程完成72=]s,不是我想要的。
所以我试图将该调用的输出存储在 res$output
中,然后在后续调用中保留它和 return。 Soooo... 我需要一个函数来修改 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
中以供稍后访问,因此后续调用 return 没什么。
我也试过这样的:
`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"
之类的赋值来调用它。
显然我也可以只 return 修改后的 res
对象,但我不喜欢那样,因为这样用户必须知道要做什么 res <- get_output(res)
并且如果他们忘记了这样做(第一次)然后输出会丢失到以太中并且永远无法恢复。不好。
非常感谢任何帮助!
我可能在这里遗漏了一些东西,但是你为什么不在创建对象后只写输出,以便它在函数第一次出现时就在那里returns?
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."
从 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."