# 第 21 章 函数式编程2

library(tidyverse)
## Warning: package 'ggplot2' was built under R version 4.2.3
## Warning: package 'tibble' was built under R version 4.2.3
## Warning: package 'tidyr' was built under R version 4.2.2
## Warning: package 'readr' was built under R version 4.2.2
## Warning: package 'purrr' was built under R version 4.2.2
## Warning: package 'dplyr' was built under R version 4.2.3
## Warning: package 'stringr' was built under R version 4.2.2
## Warning: package 'lubridate' was built under R version 4.2.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.2     ✔ readr     2.1.4
## ✔ forcats   1.0.0     ✔ stringr   1.5.0
## ✔ ggplot2   3.4.2     ✔ tibble    3.2.1
## ✔ lubridate 1.9.2     ✔ tidyr     1.3.0
## ✔ purrr     1.0.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

20 章， 我们学习了如何使用map()函数迭代一个向量（或列表），并对其元素施以函数。

## 21.1 map2()

map2()函数和map()函数类似，不同在于map2()接受两个的向量，这两个向量必须是等长

map()函数使用匿名函数，可以用 . 代表输入向量的每个元素。在map2()函数， .不够用，所有需要需要用 .x 代表第一个向量的元素，.y代表第二个向量的元素

x <- c(1, 2, 3)
y <- c(4, 5, 6)

map2(x, y, ~ .x + .y)
## [[1]]
## [1] 5
##
## [[2]]
## [1] 7
##
## [[3]]
## [1] 9

tibble的每一列都是向量，所以可以把map2()放在mutate()函数内部，对tibble的多列同时迭代

df <-
tibble(
a = c(1, 2, 3),
b = c(4, 5, 6)
)

df %>%
mutate(min = map2_dbl(a, b, ~min(.x, .y)))
## # A tibble: 3 × 3
##       a     b   min
##   <dbl> <dbl> <dbl>
## 1     1     4     1
## 2     2     5     2
## 3     3     6     3

df %>%
mutate(min = map2_dbl(a, b, min))
## # A tibble: 3 × 3
##       a     b   min
##   <dbl> <dbl> <dbl>
## 1     1     4     1
## 2     2     5     2
## 3     3     6     3

df %>%
rowwise() %>%
mutate(min = min(a, b)) %>%
ungroup()
## # A tibble: 3 × 3
##       a     b   min
##   <dbl> <dbl> <dbl>
## 1     1     4     1
## 2     2     5     2
## 3     3     6     3

## 21.2 pmap()

pmap()函数稍微有点不一样的地方:

• map()map2()函数，指定传递给函数f的向量，向量各自放在各自的位置上
• pmap()需要将传递给函数的向量名，先装入一个list()中, 再传递给函数f

map2_dbl(x, y, min)
## [1] 1 2 3
pmap_dbl(list(x, y), min)
## [1] 1 2 3

### 21.2.1 用在tibble

tibble本质上就是list，这种结构就是pmap()所需要的，因此，直接应用函数即可。

tibble(
a = c(50, 60, 70),
b = c(10, 90, 40),
c = c(1, 105, 200)
) %>%
pmap_dbl(min)
## [1]  1 60 40

### 21.2.2 匿名函数

pmap()可以接受多个向量，因此在pmap()种使用匿名函数，就需要一种新的方法来标识每个向量。

pmap(
list(1:5, 5:1, 2), ~ ..1 + ..2 - ..3
)
## [[1]]
## [1] 4
##
## [[2]]
## [1] 4
##
## [[3]]
## [1] 4
##
## [[4]]
## [1] 4
##
## [[5]]
## [1] 4

### 21.2.3 命名函数

params <- tibble::tribble(
~ n, ~ min, ~ max,
1L,     0,     1,
2L,    10,   100,
3L,   100,  1000
)

pmap(params, ~runif(n = ..1, min = ..2, max = ..3))
## [[1]]
## [1] 0.05862021
##
## [[2]]
## [1] 26.21417 86.82195
##
## [[3]]
## [1] 386.8337 558.0976 604.6650

pmap(params, runif)
## [[1]]
## [1] 0.09750563
##
## [[2]]
## [1] 85.51192 39.48302
##
## [[3]]
## [1] 879.0590 638.0751 596.4989

• 输入列表的元素，其个数要与函数的参数个数一致
• 输入列表的元素，其变量名也要与函数的参数名一致

## 21.3 其他purrr函数

### 21.3.1 Map functions that output tibbles

• 竖着堆积，map_dfr() (r for rows)
• 并排堆放 map_dfc() (c for columns)

### 21.3.2 Walk and friends

walk()函数与map()系列函数类似，但应用场景不同，map()在于执行函数操作，而walk() 保存记录数据（比如print(),write.csv(), ggsave()），常用于保存数据和生成图片。比如我们用map()生成系列图片，

plot_rnorm <- function(sd) {
tibble(x = rnorm(n = 5000, mean = 0, sd = sd)) %>%
ggplot(aes(x)) +
geom_histogram(bins = 40) +
geom_vline(xintercept = 0, color = "blue")
}

plots <-
c(5, 1, 9) %>%
map(plot_rnorm)

plots %>%
walk(print)

map()函数是一定要返回列表的，但walk()看上去函数没有返回值，实际上它返回的就是它的输入，只是用户不可见而已。