[英]Compose functions with function operators does not work as expected
In the following example I created the add_timing
function operator. 在以下示例中,我创建了
add_timing
函数运算符。 The input is a function (say mean
) and it returns a function that does the same as mean
, but reports on how long it took for the function to complete. 输入是一个函数(例如
mean
),它返回一个与mean
相同的函数,但报告完成该函数所花费的时间。 See the following example: 请参见以下示例:
library(pryr)
add_timing = function(input_function, specific_info) {
if (missing(specific_info)) specific_info = function(l) 'That'
function(...) {
relevant_value = specific_info(list(...))
start_time = Sys.time()
res = input_function(...)
cat(sprintf('%s took', relevant_value), difftime(Sys.time(), start_time, units = 'secs'), 'sec', '\n')
res
}
}
timed_mean = add_timing(mean)
# > timed_mean(runif(10000000))
# That took 0.4284899 sec
# [1] 0.4999762
Next I tried to use pryr::compose
to create the same timed_mean
function (I like the syntax): 接下来,我尝试使用
pryr::compose
创建相同的timed_mean
函数(我喜欢语法):
timed_mean_composed = pryr::compose(add_timing, mean)
But this does get me the required output: 但这确实为我提供了所需的输出:
# > timed_mean_composed(runif(100))
# function(...) {
# relevant_value = specific_info(list(...))
# start_time = Sys.time()
# res = input_function(...)
# cat(sprintf('%s took', relevant_value), difftime(Sys.time(), start_time, units = 'secs'), 'sec', '\n')
# res
# }
It seems that the compose operation does not lead to the add_timing
function actually being executed. 似乎compose操作不会导致实际执行
add_timing
函数。 Only after calling the function, the new timed_mean_compose
actually shows the correct function output. 仅在调用函数之后,新的
timed_mean_compose
实际上会显示正确的函数输出。
Based on the following example from Advanced R by @HadleyWickham I expected this to work as I used it (see below for an excerpt): 基于@HadleyWickham的Advanced R提供的以下示例 ,我希望它可以在使用时起作用(摘录见下文):
dot_every <- function(n, f) {
i <- 1
function(...) {
if (i %% n == 0) cat(".")
i <<- i + 1
f(...)
}
}
download <- pryr::compose(
partial(dot_every, 10),
memoise,
partial(delay_by, 1),
download_file
)
Where the dot_every
function operator is used in the same way I use add_timing
above. 在使用
dot_every
函数运算符的方式与我add_timing
上面使用add_timing
方式相同。
What am I missing? 我想念什么?
The difference is that in your first attempt, you are calling 不同之处在于您第一次尝试时
(add_timing(mean))(runif(1e7)
and with the compose
syntax you are calling something more similar to 并使用
compose
语法调用更类似于
add_timing(mean(runif(1e7))
These are not exactly equivalent. 这些并不完全等效。 Actually, the
pryr
compose function is really expanding the syntax to something more like 实际上,
pryr
compose函数实际上是在将语法扩展到更像
x <- runif(1e7)
x <- mean(x)
x <- add_timing(x)
Maybe looking at this will help 也许看一下会有所帮助
a <- function(x) {print(paste("a:", x));x}
b <- function(x) {print(paste("b:", x));x}
x <- pryr::compose(a,b)(print("c"))
# [1] "c"
# [1] "b: c"
# [1] "a: c"
Notice how a
isn't called until after b
. 注意
a
直到b
之后才被调用。 This means that a
would have no way to time b
. 这意味着
a
将无法计时b
。 compose
would not be an appropriate way to create a timer wrapper. compose
不是创建计时器包装的适当方法。
The issue is that pryr::compose
is aimed at doing something completely different from what you're trying to do in your initial example. 问题在于
pryr::compose
旨在执行与您在最初示例中尝试执行的操作完全不同的操作。 You want to create a function factory (called add_timing
), which will take a function as input and return a new function as output that does the same thing as the input function but with an additional time printing. 您想创建一个函数工厂(称为
add_timing
),它将把一个函数作为输入,并返回一个新函数作为输出,该函数与输入函数具有相同的功能,但需要更多的打印时间。 I would write that as follows: 我会这样写:
add_timing <- function(FUN) { function(...) { print(system.time(r <- FUN(...))); r }}
mean(1:5)
# [1] 3
add_timing(mean)(1:5)
# user system elapsed
# 0 0 0
# [1] 3
The compose
function, by contrast, returns a function that represents a series of functions to be evaluated in sequence. 相反,
compose
函数返回一个函数,该函数表示要按顺序评估的一系列函数。 The examples in ? compose
的例子
? compose
? compose
are helpful here. 在这里
? compose
很有帮助。 Here's an example that builds on that: 这是一个基于此的示例:
add1 <- function(x) x + 1
times2 <- function(x) x * 2
# the following two are identical:
add1(1)
# [1] 2
compose(add1)(1)
# [1] 2
# the following two are identical:
times2(1)
# [1] 2
compose(times2)(1)
# [1] 2
compose
becomes useful for nesting, when the order of nesting is important: 当嵌套顺序很重要时,
compose
对于嵌套很有用:
add1(times2(2))
# [1] 5
compose(add1, times2)(2)
# [1] 5
times2(add1(2))
# [1] 6
compose(times2, add1)(2)
# [1] 6
This means that the reason your example does not work is because your functions are not actually nested in the way that compose
is intended to work. 这意味着您的示例不起作用的原因是,您的函数实际上并未按照
compose
的预期工作方式进行嵌套。 In your example, you're asking system.time
to, for example, calculate the time to evaluate 3
(the output of mean
) rather than the time to evaluate mean(1:5)
. 在您的示例中,您要求
system.time
例如,计算评估3
的时间( mean
的输出),而不是评估mean(1:5)
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.