R函数式编程
Functional Programming in R
R语言的循环很慢,同时语法有很复杂,陷阱很多。举例来说,逐行遍历一个矩阵有两种写法:
(1)
for (i in seq_len(nrow(m))){print(i)}
(2)
for (i in 1:nrow(m)){print(i)}
这两个写法那个对?或者两个都对?
熟悉R的用户自然会用(1),因为当m矩阵的行数为零时,只有(1)的结果是正确的:
> m <- matrix(, nr = 0, nc = 0) > for (i in seq_len(nrow(m))){print(i)} > for (i in 1:nrow(m)){print(i)} [1] 1 [1] 0
为了避免手动写循环,一种折中的解决方案是用Filter, Reduce, Map等函数式编程的概念。
这些函数可以比较自然的处理列表(list)或者向量(vector)类型。举个例子:
l <- as.list(seq(9, 1, -1)) d <- data.frame(a = c(1, 2), b = c(3, 4)) m <- matrix(seq(9, 1, -1), 3, 3) v <- seq(9, 1, -1) isEven <- function(x) { x %% 2 == 0 } isEven(1:3) Filter(isEven, l) ## Filter(isEven, d) ## this does not work Filter(isEven, m) Filter(isEven, v) Position(isEven, l) # Position(isEven, d) ## this does not work Position(isEven, m) Position(isEven, v) Find(isEven, l) # Find(isEven, d) ## this does not work Find(isEven, m) Find(isEven, v)
可以看到,对于列表(list)或者向量(vector)类型,R内建的函数式函数(更多的见[1])还是很方便的。
如果想把函数式编程玩出更多花样,可以看看Hadley的新书Adv-R(参见[2])。
这里还有个问题,这种函数式的写法速度如何?下面的代码告诉我们函数式编程节省了写程序的时间,在某些情况下也许也能节省计算时间。
library(microbenchmark) v <- seq(10000) m1 <- function(v) { v[v%%2 == 0] } m2 <- function(v) { res <- vector() for(i in v) { if (i %% 2 == 0) { res <- c(res, i) } } res } m3 <- function(v) { Filter(isEven, v) } microbenchmark(res1 <- m1(v), res2 <- m2(v), res3 <- m3(v), times = 1000)
下面是结果:
Unit: microseconds expr min lq mean median uq max neval res1 <- m1(v) 341.038 348.526 529.9475 356.225 380.6965 58359.46 1000 res2 <- m2(v) 33687.987 38433.148 48014.3656 39791.000 41751.3955 303604.96 1000 res3 <- m3(v) 6178.547 6648.369 8493.9365 6999.976 7445.0725 124781.90 1000
在上面的例子中,
第一种是向量化的赋值,速度最快。
第二种是用循环,速度最慢,
第三种是函数式,速度居中。
不过据我的经验,当v的维数很小时(比如只有100个数),函数式相对于循坏的优势越来越小,甚至比循环要慢。
题外话:据说R的核心是类似于Scheme,也是一种函数式语言。但是在速度上对函数式编程的支持看来还有很大的提升空间。
[1]https://stat.ethz.ch/R-manual/R-devel/library/base/html/funprog.html
[2]Advanced R http://adv-r.had.co.nz/Functional-programming.html