和其它编程语言一样,R 中也存在着高效执行重复任务的结构,即循环,也称迭代。在 R 中存在着两种重要的迭代方式:命令式编程和函数式编程。其它编程语言中常见的如 for 循环、while 循环(或类似于这些循环)等循环结构,在 R 中同样存在,属于命令式编程。但在 R 中还存在着另外一种循环方式,即函数式编程。R 有一些包可以实现该功能,例如 purrr
包。该包是 tidyverse
包的核心 R 包之一。
for 循环
while 循环
映射循环
单向量映射
R 中经常会存在这样一种模式:先对向量进行循环,然后对每个元素进行处理,最后保存结果。purrr
包中存在一个函数族可以完成以上操作:
函数 | 作用 |
---|---|
map() | 输出列表 |
map_lgl() | 输出逻辑型向量 |
map_int() | 输出整型向量 |
map_dbl() | 输出双精度型向量 |
map_chr() | 输出字符型向量 |
映射函数的主要优势不是速度,而是简洁
具体使用方式如下所示:
1 | > df <- tibble(x = c(3, 4, 5), y = c(6, 8, 10), z = c(5, 12, 13)) |
映射函数除了可以对向量等多元素结构中的元素进行函数处理外,还可以给用来处理的函数附带参数,例如:
1 | > mu <- list(0, 1, 10) |
上例会生成一个列表,其中的元素为服从均值分别为 0、1、10 的正态分布(标准差为 1,样本容量为 100000 )的随机数数列。
map()
函数等映射函数均可以附带多个参数,列在处理函数名之后
多向量映射
映射函数 map()
可以进行一个向量的迭代映射处理。对于多个向量的情况,需要使用其它的映射函数。例如可以使用 map2()
函数进行双向量的映射:
1 | > mu <- list(0, 1, 10) |
类似地,上例会生成一个列表,其中的元素为服从均值分别为 0、1、10 ,标准差分别为 1、5、10 的正态分布(样本容量为 100000 )的随机数数列。
在此基础上,若想对多向量进行迭代映射处理,可以使用 pmap()
函数。例如:
1 | > n <- list(200, 300, 500) |
pmap()
函数与 map()
等映射函数稍有区别,它接受一个多向量构成的列表作为第一参数,而不是接受多个向量作为参数
pmap()
函数的第一参数中列表的各个元素名推荐与处理函数的参数名一致,否则该函数会将列表内与处理函数的参数名不一致的名称对应的元素,
多向量多函数映射
在多向量映射的基础上,还可以进一步地对多个操作函数进行映射。实现该操作的函数为 invoke_map()
,例如:
1 | > funclist <- list(runif, rpois, rnorm) |
上例会生成一个列表,列表中包含三个元素。第一个元素为服从最小值为 -1,最大值为 1 ,样本容量为 100 的均匀分布的随机数据列表;第二个元素为服从期望和方差为 10 ,样本容量为 100 的泊松分布的随机数据列表;第三个元素为服从标准差为 4 ,均值为 5 ,样本容量为 100 的正态分布的随机数据列表。
与单函数映射不同,invoke_map()
函数的第二参数(即操作函数的对应参数集列表)是元素为列表的列表。即第二参数的各个元素对应着第一参数(即操作函数列表)的
概括地讲,invoke_map()
函数的第二参数是操作函数参数集列表,其元素是操作函数参数集,其元素的元素是操作函数参数
基于上述,多函数映射的第二参数必须注意操作函数参数名
游走函数
当进行迭代的目的是函数的副作用而不是返回值时,应该使用游走函数而不是映射函数。实现该操作的函数为 walk()
,例如:
1 | > x <- list(1, "R", TRUE) |
类似地,游走函数也存在着函数 walk2()
和 pwalk()
函数。
以上三个函数除了完成操作函数的副作用外,这些游走函数本身也具有返回值,为其第一参数(若无进行修改的话)
针对上例,walk(x, print)
与 x
的值是一样的
预测函数
保留元素
可以使用 keep()
函数保留符合条件的元素,例如:
1 | > rep(10, 10) %>% map(sample, 5) %>% keep(function(x) mean(x) > 6) |
丢弃元素
可以使用 discard()
函数丢弃符合条件的元素,例如:
1 | > rep(10, 10) %>% map(sample, 5) %>% discard(function(x) mean(x) > 6) |
列表元素预测
对列表元素可以使用以下函数进行预测:
函数 | 作用 |
---|---|
every() | 判断预测值是否对所有元素为真 |
some() | 判断预测值是否对某个元素为真 |
none() | 判断预测值是否对所有元素为假 |
例如:
1 | > y <- list(0:10, 5.5) |
查找元素
可以使用 detect()
函数找出预测为真的第一个元素,使用 detect_index()
函数返回预测为真的第一个元素的索引。例如:
1 | > is_even <- function(x) x %% 2 == 0 |
可以使用 head_while()
和 tail_while()
函数分别从列表或原子型向量的开头和结尾找出预测为真的元素,直到预测为假为止。例如:
1 | > numlist <- c(1, 2, 3, 4, 3, 4, 5, 4, 3, 4, 3, 2, 1) |
规约与累计
可以使用归约函数 reduce()
将列表中的元素按从前向后的顺序用二元函数或二元运算符进行迭代运算。例如:
1 | > physical_properties <- list( |
累计函数 accumulate()
的作用与归约函数相似,区别在于它会将迭代操作过程显示出来。例如:
1 | > physical_properties <- list( |