Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a list of functions using a loop in R

In this introduction to functional programming, the author Hadley Wickham creates the following function factory:

power <- function(exponent) {
  function(x) {
    x ^ exponent
 }
}

He then shows how this function can be used to define other functions, such as

square <- power(2)
cube <- power(3)

Now suppose I wanted to create these functions simultaneously via the following loop:

ftns <- lapply(2:3, power)

This doesn't seem to work, as 3 gets assigned to the exponent for all entries of the list:

as.list(environment(ftns[[1]]))

$exponent
[1] 3

Can someone please help me understand what's wrong with this code?

Thanks!

like image 661
David Avatar asked Feb 02 '16 22:02

David


1 Answers

What you're seeing is a consequence of R's use of promises to implement lazy argument evaluation. See Promise objects.

The problem is that in the power() function the exponent argument is never evaluated, meaning the underlying promise is never called (at least not until the generated function is evaluated).

You can force the promise to be evaluated like this:

power <- function(exponent) { exponent; function(x) x^exponent; };
ftns <- lapply(2:3,power);
sapply(ftns,function(ftn) environment(ftn)$exponent);
## [1] 2 3

Without the exponent; statement to force evaluation of the promise, we see the issue:

power <- function(exponent) { function(x) x^exponent; };
ftns <- lapply(2:3,power);
sapply(ftns,function(ftn) environment(ftn)$exponent);
## [1] 3 3

Aha! The R changelog shows that this behavior was changed in 3.2.0:

* Higher order functions such as the apply functions and Reduce() now force arguments to the functions they apply in order to eliminate undesirable interactions between lazy evaluation and variable capture in closures. This resolves PR#16093.

It's classified as a new feature.

like image 144
bgoldst Avatar answered Nov 18 '22 17:11

bgoldst