简体   繁体   English

使用函数运算符编写函数无法按预期方式工作

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM