2017-06-10 29 views
0

这是我创建的一个测试函数,用于了解lapply和闭包如何交互。我的目标是依次遍历runif函数并打印每个迭代。闭包的执行顺序

Mod_delay_by <- function(delay,f){ 
    function(x,...){ 
    Sys.sleep(delay) 
    cat(".") 
    #f(x,...) 
    cat("Processing",x,"\n",sep = "") 
    f(x,...) 
    } 
} 

现在让我们使用lapply上面的函数调用:

lapply(1:3,Mod_delay_by(0.1,runif)) 

当我致电上述功能,我得到下面的示例输出:

.Processing1 
.Processing2 
.Processing3 
[[1]] 
[1] 0.835246 

[[2]] 
[1] 0.1370997 0.4350032 

[[3]] 
[1] 0.1174749 0.4087628 0.7222604 

我很惊讶,因为所有三个“处理”在执行runif之前出现。相反,我本来期望的输出是...

.Processing1 
[[1]] 
[1] 0.835246 

.Processing2 
[[2]] 
[1] 0.1370997 0.4350032 

.Processing3 
[[3]] 
[1] 0.1174749 0.4087628 0.7222604 

...因为runif(即功能f)立即通过调用“.Processing”开头

是否有人可以解释为什么lapply首先完成打印全部三个Processing,然后打印全部三个runifs

在此先感谢您的帮助。

+2

'lapply'汇编函数返回的任何东西(最后一行,除非你使用显式的'return'语句)并返回它。 '猫'调用是副作用,因此在调用时会被打印,而不是返回匿名函数(当返回的对象被'lapply'存储时)或'lapply'完成时(当它将所有结果组装到一个列表中并返回它,如果未分配则导致它打印)。 – alistaire

回答

1

显然,OP预计f()输出应该是打印到控制台风ow在每个电话中都像cat()一样。

到时会发生什么功能直接从控制台窗口调用类似:

> Mod_delay_by(0.1, runif)(2) 
.Processing2 
[1] 0.4519318 0.2331198 

请注意,我们将不会看到任何印刷(除的cat()输出)如果输出分配给变量:

> result <- Mod_delay_by(0.1, runif)(2) 
.Processing2 

所以,如果你从控制台窗口调用一个函数,并且输出没有分配给它打印到控制台窗口的变量。现在


f()正在从函数的主体内被调用。这种情况是不同的。

而不是被打印到控制台窗口,的f()输出被返回给调用者。帮助文件?"function"表示如果在不调用return的情况下到达函数的结尾,则返回上一个计算表达式的值。

这可通过写

return(f(x, ...)) 

因此,只有cat()打印到控制台中作出明确规定。 f()的输出返回到调用lapply()函数,该函数收集列表中的所有输出。最后,这个列表仅仅被打印到控制台,因为它没有被分配给变量

如果输出分配给一个变量,我们将不会看到除了印什么cat()打印到控制台东西。

> result <- lapply(1:3, Mod_delay_by(0.1, runif)) 
.Processing1 
.Processing2 
.Processing3 

总之,R试图节省打字,但结果取决于上下文:

  • 在控制台窗口中,f()没有分配意味着print(f())

  • 在函数的主体中,f()作为最后评估的表达式意味着return(f())

3

默认情况下,函数不会将任何内容打印到控制台。

.Processing1 
.Processing2 
.Processing3 
[[1]] 
[1] 0.835246 

[[2]] 
[1] 0.1370997 0.4350032 

[[3]] 
[1] 0.1174749 0.4087628 0.7222604 

在这里,你是因为你使用的cat和休息从lapply输出来获得

.Processing1 
.Processing2 
.Processing3 

如果您改变了定义函数这样

Mod_delay_by <- function(delay,f){ 
    function(x,...){ 
    Sys.sleep(delay) 
    cat(".") 
    #f(x,...) 
    cat("Processing",x,"\n",sep = "") 
    print(f(x,...)) 
    } 
} 

,然后运行

lapply(1:3,Mod_delay_by(0.1,runif)) 

你会得到

#.Processing1 
#[1] 0.5281055 
#.Processing2 
#[1] 0.892419 0.551435 
#.Processing3 
#[1] 0.4566147 0.9568333 0.4533342 
#[[1]] 
#[1] 0.5281055 
# 
#[[2]] 
#[1] 0.892419 0.551435 
# 
#[[3]] 
#[1] 0.4566147 0.9568333 0.4533342 
+0

非常感谢您的帮助。快速问:为什么'cat'完成了所有三个迭代的打印,然后在打印后打印'lapply',如最初在答案中所述?我没有完全理解这部分。我会很感激你的想法。另一种思考我的问题的方法是:如果'lapply'已经完成执行(从“处理”中可以看出),为什么我们看到最后打印的数字?是否我们重复遍历该函数两次? – watchtower

+0

不,你弄错了。从'lapply'帮助页面引用:'lapply返回一个与X相同长度的列表,其中的每个元素都是将FUN应用于X的相应元素的结果“,即'lapply'将在末尾返回一个列表'function'的执行时间。我认为现在很清楚'lapply'的行为。 – user2100721

+0

再次感谢您的见解,研究和帮助的意愿。我很好奇。你是如何得出结论的:lapply将在最后而不是在执行期间返回列表?我阅读,重读和重读引用的声明,但我没有关注。引用的陈述,如果我没有错,只会声明'lapply'返回'xyz'长度列表。它在哪里谈论打印的时机?我好奇。 – watchtower