watchtower
watchtower

Reputation: 4298

Sequence of execution for closures

Here's a test function I created to learn how lapply and closures interact. My objective is to iterate over runif function sequentially and print each iteration.

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

Now let's use lapply to call above function:

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

When I call above function, I get the following sample output:

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

[[2]]
[1] 0.1370997 0.4350032

[[3]]
[1] 0.1174749 0.4087628 0.7222604

I am surprised because all the three ".Processing" come before execution of runif. Instead, I would have expected the output to be ...

.Processing1
[[1]]
[1] 0.835246

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

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

...because runif (i.e. function f) is immediately preceded by a call to ".Processing"

Can someone please explain why lapply first finishes printing all the three Processing, and then prints all three runifs?

Thanks in advance for any help.

Upvotes: 0

Views: 485

Answers (2)

Uwe
Uwe

Reputation: 42544

Apparently, the OP expects the output of f() should be printed to the console window in each call like cat() does.

Similar to what happens when the function is called directly from the console window:

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

Please note that we won't see anything printed (except the output of cat()) if the output is assigned to a variable:

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

So, if you call a function from the console window and the output is not assigned to a variable it is printed to the console window.


Now, f() is being called from within the body of a function. This situation is different.

Instead of being printed to the console window, the output of f() is returned to the caller. The help file ?"function" says If the end of a function is reached without calling return, the value of the last evaluated expression is returned.

This can be made more explicit by writing

return(f(x, ...))

So, only cat()prints to console. The output of f() is returned to the calling lapply() function which collects all output in a list. Finally, this list is printed to the console only because it is not assigned to a variable.

If the output is assigned to a variable, we won't see anything printed except what cat() prints to the console.

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

To summarize, R is trying to save typing but the outcome depends on the context:

  • In the console window, f() without assignment implies print(f()).

  • In the body of a function, f() as last evaluated expression implies return(f()).

Upvotes: 1

user2100721
user2100721

Reputation: 3587

Function does not print anything to console by default.

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

[[2]]
[1] 0.1370997 0.4350032

[[3]]
[1] 0.1174749 0.4087628 0.7222604

Here you are getting

.Processing1
.Processing2
.Processing3

because you are using cat and rest are coming from the lapply output.

If you change your defined function like this

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

and then run

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

you will get

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

Upvotes: 3

Related Questions