2015-08-31 89 views
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 
#  } 

看來撰寫操作不會導致實際執行的add_timing函數。只有在調用該函數之後,新的timed_mean_compose纔會顯示正確的函數輸出。

通過@HadleyWickham基礎上following example從先進的R I預計這個工作,我用它(請參閱下面的摘錄):

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

我錯過了什麼?

回答

3

不同的是,在你的第一次嘗試,您呼叫

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

,並與compose語法,你在呼喚更多類似

add_timing(mean(runif(1e7)) 

這些都不是完全等價的東西。實際上,pryr構建功能真的擴大語法來更多的東西一樣

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是如何不叫,直到b後。這意味着a將無法​​計時bcompose不適合創建定時器包裝。

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)的時間。

+0

感謝您的反饋意見。我的問題是爲什麼'dot_every'功能工廠確實工作,而我的工作沒有。 –

+0

@PaulHiemstra我認爲我會遠離'dot_every'這個例子,因爲考慮到使用'partial'和'dot_every'使用賦值到一個函數環境的事實,這裏有很多事情要做。這是相當模糊的;作爲例子,運行:'f < - dot_every(3,mean);環境(F)$ I; F(1); F(1); F(1);環境(F)$ i' – Thomas