1

在以下示例中,我创建了add_timing函数运算符。输入是一个函数(比如mean),它返回一个与 执行相同操作的函数mean,但会报告函数完成所需的时间。请参见以下示例:

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

接下来我尝试使用pryr::compose创建相同的timed_mean函数(我喜欢语法):

timed_mean_composed = pryr::compose(add_timing, mean)

但这确实为我提供了所需的输出:

# > 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
#      }

似乎 compose 操作并没有导致add_timing函数实际被执行。只有在调用函数之后,new才timed_mean_compose真正显示正确的函数输出。

根据@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
)

函数dot_every运算符的使用方式与我add_timing上面使用的方式相同。

我错过了什么?

4

2 回答 2

3

不同之处在于,在您的第一次尝试中,您正在调用

(add_timing(mean))(runif(1e7)

并且使用compose您调用的语法更类似于

add_timing(mean(runif(1e7))

这些并不完全等价。实际上,pryrcompose 函数实际上是将语法扩展为更像

x <- runif(1e7)
x <- mean(x)
x <- add_timing(x)

也许看看这个会有所帮助

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"

请注意,a直到 after 才被调用b。这意味着a就没有办法时间了bcompose不是创建计时器包装器的合适方法。

于 2015-08-31T19:05:17.450 回答
1

问题在于,pryr::compose它旨在做一些与您在最初示例中尝试做的事情完全不同的事情。您想要创建一个函数工厂(称为add_timing),它将一个函数作为输入并返回一个新函数作为输出,该函数与输入函数执行相同的操作,但需要额外的时间打印。我会这样写:

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

相比之下,该compose函数返回一个函数,该函数表示要按顺序计算的一系列函数。中的示例在? compose这里很有帮助。这是一个基于此的示例:

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当嵌套的顺序很重要时,嵌套变得有用:

add1(times2(2))
# [1] 5
compose(add1, times2)(2)
# [1] 5

times2(add1(2))
# [1] 6
compose(times2, add1)(2)
# [1] 6

这意味着您的示例不起作用的原因是因为您的函数实际上并没有按照compose预期的方式嵌套。例如,在您的示例中,您要求system.time计算评估时间3(的输出mean)而不是评估时间mean(1:5)

于 2015-08-31T19:06:14.980 回答