向函数传递一个向量或未定义数量的参数
pass a function a vector or undefined number of arguments
我希望能够通过 ...
向函数传递未定义数量的参数,但也希望能够向它传递 vector
。这是一个愚蠢的例子:
library(tidyverse)
df <- data.frame(gear = as.character(unique(mtcars$gear)),
id = 1:3)
myfun <- function(...) {
ids_lst <- lst(...)
df2 <- bind_rows(map(ids_lst, function(x)
mtcars %>%
filter(gear == x) %>%
select(mpg)), .id = "gear") %>%
left_join(df)
df2
}
#these all work:
myfun(3)
myfun(3, 4)
myfun(3, 4, 5)
尽管向它传递一个向量不起作用:
myvector <- unique(mtcars$gear)
myfun(myvector)
问题出在函数收集参数的方式以及它如何 returns 它们:
myfun_lst <- function(...) {
ids_lst <- lst(...)
ids_lst
}
myfun_lst(3, 4, 5)
# $`3`
# [1] 3
# $`4`
# [1] 4
# $`5`
# [1] 5
myfun_lst(myvector)
# $myvector
# [1] 4 3 5
我认为解决方法是测试输入是否为 vector
,例如:
myfun_final <- function(...) {
if(is.vector(...) & !is.list(...)) {
ids_lst <- as.list(...)
names(ids_lst) <- (...)
} else {
ids_lst <- lst(...)
}
df2 <- bind_rows(map(ids_lst, function(x)
mtcars %>%
filter(gear == x) %>%
select(mpg)), .id = "gear") %>%
left_join(df)
df2
}
现在,向函数传递一个向量是可行的,但收集参数却不行:
myfun_final(3, 4, 5)
myfun_final(myvector)
有什么好的方法可以解决这个问题?
谢谢
如何测试 ...
的长度是否为 1 以及传递的唯一参数是否为向量?如果不是这样,则考虑 ...
缩放器列表并使用 lst(...)
.
捕获它们
myfun_final <- function(...) {
if (...length() == 1L && is.vector(..1))
ids_lst <- `names<-`(..1, ..1)
else
ids_lst <- lst(...)
df2 <- bind_rows(map(ids_lst, function(x)
mtcars %>%
filter(gear == x) %>%
select(mpg)), .id = "gear") %>%
left_join(df)
df2
}
测试
> myfun_final(3)
Joining, by = "gear"
gear mpg id
1 3 21.4 2
2 3 18.7 2
3 3 18.1 2
4 3 14.3 2
5 3 16.4 2
6 3 17.3 2
7 3 15.2 2
8 3 10.4 2
9 3 10.4 2
10 3 14.7 2
11 3 21.5 2
12 3 15.5 2
13 3 15.2 2
14 3 13.3 2
15 3 19.2 2
> myfun_final(3,4,5)
Joining, by = "gear"
gear mpg id
1 3 21.4 2
2 3 18.7 2
3 3 18.1 2
4 3 14.3 2
5 3 16.4 2
6 3 17.3 2
7 3 15.2 2
8 3 10.4 2
9 3 10.4 2
10 3 14.7 2
11 3 21.5 2
12 3 15.5 2
13 3 15.2 2
14 3 13.3 2
15 3 19.2 2
16 4 21.0 1
17 4 21.0 1
18 4 22.8 1
19 4 24.4 1
20 4 22.8 1
21 4 19.2 1
22 4 17.8 1
23 4 32.4 1
24 4 30.4 1
25 4 33.9 1
26 4 27.3 1
27 4 21.4 1
28 5 26.0 3
29 5 30.4 3
30 5 15.8 3
31 5 19.7 3
32 5 15.0 3
> myfun_final(c(3,4,5))
Joining, by = "gear"
gear mpg id
1 3 21.4 2
2 3 18.7 2
3 3 18.1 2
4 3 14.3 2
5 3 16.4 2
6 3 17.3 2
7 3 15.2 2
8 3 10.4 2
9 3 10.4 2
10 3 14.7 2
11 3 21.5 2
12 3 15.5 2
13 3 15.2 2
14 3 13.3 2
15 3 19.2 2
16 4 21.0 1
17 4 21.0 1
18 4 22.8 1
19 4 24.4 1
20 4 22.8 1
21 4 19.2 1
22 4 17.8 1
23 4 32.4 1
24 4 30.4 1
25 4 33.9 1
26 4 27.3 1
27 4 21.4 1
28 5 26.0 3
29 5 30.4 3
30 5 15.8 3
31 5 19.7 3
32 5 15.0 3
当然,您可以更改您的函数,使其同时适用于常规参数 myfun(3, 4, 5)
和向量 myfun(myvector)
,如上面的答案所示。
另一种选择是通过使用 bang bang bang 运算符取消引号来使用参数拼接 !!!
。此运算符仅在某些 {rlang} 和 {tidyverse} 函数中受支持。在您的示例中,您评估了支持参数拼接的 purrr::map
内的点 ...
。因此可能不需要重写您的函数:
library(tidyverse)
# your original function:
myfun <- function(...) {
ids_lst <- lst(...)
df2 <- bind_rows(map(ids_lst, function(x)
mtcars %>%
filter(gear == x) %>%
select(mpg)), .id = "gear") %>%
left_join(df)
df2
}
myvector <- unique(mtcars$gear)
myfun(!!! myvector) # works
#> Joining, by = "gear"
#> gear mpg id
#> 1 4 21.0 1
#> 2 4 21.0 1
#> 3 4 22.8 1
#> 4 4 24.4 1
#> 5 4 22.8 1
#> 6 4 19.2 1
#> 7 4 17.8 1
#> 8 4 32.4 1
#> 9 4 30.4 1
#> 10 4 33.9 1
#> ...
myfun(3, 4, 5) # works
#> Joining, by = "gear"
#> gear mpg id
#> 1 3 21.4 2
#> 2 3 18.7 2
#> 3 3 18.1 2
#> 4 3 14.3 2
#> 5 3 16.4 2
#> 6 3 17.3 2
#> 7 3 15.2 2
#> 8 3 10.4 2
#> 9 3 10.4 2
#> 10 3 14.7 2
#> ...
由 reprex package (v0.3.0)
于 2021-12-30 创建
您可以阅读有关使用 bang bang bang 运算符取消引用的更多信息 here。
最后,您应该考虑您的功能的用户。如果您是唯一的用户,请选择适合您的任何内容。如果有其他用户,您应该考虑他们期望该功能如何工作。用户可能不希望一个函数可以处理多个参数,或者同时在一个向量中提供这些参数。在 tidyverse 参数中,用 !!!
拼接是一个公认的概念。在基础 R 中,我们通常会使用 do.call("myfun", as.list(myvector))
来实现类似的效果。
要添加另一个选项:
purrr 包有一系列 lift
函数,可用于改变函数采用的参数类型。最突出的是 lift_dl
,它将一个以点为参数的函数转换为一个以列表或向量为参数的函数。这也可以用来解决问题而不需要重写函数:
lift_dl(myfun)(myvector)
#> Joining, by = "gear"
#> gear mpg id
#> 1 4 21.0 1
#> 2 4 21.0 1
#> 3 4 22.8 1
#> 4 4 24.4 1
#> 5 4 22.8 1
#> 6 4 19.2 1
#> 7 4 17.8 1
#> 8 4 32.4 1
#> 9 4 30.4 1
#> 10 4 33.9 1
#> ...
由 reprex package (v0.3.0)
创建于 2022-01-01
我希望能够通过 ...
向函数传递未定义数量的参数,但也希望能够向它传递 vector
。这是一个愚蠢的例子:
library(tidyverse)
df <- data.frame(gear = as.character(unique(mtcars$gear)),
id = 1:3)
myfun <- function(...) {
ids_lst <- lst(...)
df2 <- bind_rows(map(ids_lst, function(x)
mtcars %>%
filter(gear == x) %>%
select(mpg)), .id = "gear") %>%
left_join(df)
df2
}
#these all work:
myfun(3)
myfun(3, 4)
myfun(3, 4, 5)
尽管向它传递一个向量不起作用:
myvector <- unique(mtcars$gear)
myfun(myvector)
问题出在函数收集参数的方式以及它如何 returns 它们:
myfun_lst <- function(...) {
ids_lst <- lst(...)
ids_lst
}
myfun_lst(3, 4, 5)
# $`3`
# [1] 3
# $`4`
# [1] 4
# $`5`
# [1] 5
myfun_lst(myvector)
# $myvector
# [1] 4 3 5
我认为解决方法是测试输入是否为 vector
,例如:
myfun_final <- function(...) {
if(is.vector(...) & !is.list(...)) {
ids_lst <- as.list(...)
names(ids_lst) <- (...)
} else {
ids_lst <- lst(...)
}
df2 <- bind_rows(map(ids_lst, function(x)
mtcars %>%
filter(gear == x) %>%
select(mpg)), .id = "gear") %>%
left_join(df)
df2
}
现在,向函数传递一个向量是可行的,但收集参数却不行:
myfun_final(3, 4, 5)
myfun_final(myvector)
有什么好的方法可以解决这个问题? 谢谢
如何测试 ...
的长度是否为 1 以及传递的唯一参数是否为向量?如果不是这样,则考虑 ...
缩放器列表并使用 lst(...)
.
myfun_final <- function(...) {
if (...length() == 1L && is.vector(..1))
ids_lst <- `names<-`(..1, ..1)
else
ids_lst <- lst(...)
df2 <- bind_rows(map(ids_lst, function(x)
mtcars %>%
filter(gear == x) %>%
select(mpg)), .id = "gear") %>%
left_join(df)
df2
}
测试
> myfun_final(3)
Joining, by = "gear"
gear mpg id
1 3 21.4 2
2 3 18.7 2
3 3 18.1 2
4 3 14.3 2
5 3 16.4 2
6 3 17.3 2
7 3 15.2 2
8 3 10.4 2
9 3 10.4 2
10 3 14.7 2
11 3 21.5 2
12 3 15.5 2
13 3 15.2 2
14 3 13.3 2
15 3 19.2 2
> myfun_final(3,4,5)
Joining, by = "gear"
gear mpg id
1 3 21.4 2
2 3 18.7 2
3 3 18.1 2
4 3 14.3 2
5 3 16.4 2
6 3 17.3 2
7 3 15.2 2
8 3 10.4 2
9 3 10.4 2
10 3 14.7 2
11 3 21.5 2
12 3 15.5 2
13 3 15.2 2
14 3 13.3 2
15 3 19.2 2
16 4 21.0 1
17 4 21.0 1
18 4 22.8 1
19 4 24.4 1
20 4 22.8 1
21 4 19.2 1
22 4 17.8 1
23 4 32.4 1
24 4 30.4 1
25 4 33.9 1
26 4 27.3 1
27 4 21.4 1
28 5 26.0 3
29 5 30.4 3
30 5 15.8 3
31 5 19.7 3
32 5 15.0 3
> myfun_final(c(3,4,5))
Joining, by = "gear"
gear mpg id
1 3 21.4 2
2 3 18.7 2
3 3 18.1 2
4 3 14.3 2
5 3 16.4 2
6 3 17.3 2
7 3 15.2 2
8 3 10.4 2
9 3 10.4 2
10 3 14.7 2
11 3 21.5 2
12 3 15.5 2
13 3 15.2 2
14 3 13.3 2
15 3 19.2 2
16 4 21.0 1
17 4 21.0 1
18 4 22.8 1
19 4 24.4 1
20 4 22.8 1
21 4 19.2 1
22 4 17.8 1
23 4 32.4 1
24 4 30.4 1
25 4 33.9 1
26 4 27.3 1
27 4 21.4 1
28 5 26.0 3
29 5 30.4 3
30 5 15.8 3
31 5 19.7 3
32 5 15.0 3
当然,您可以更改您的函数,使其同时适用于常规参数 myfun(3, 4, 5)
和向量 myfun(myvector)
,如上面的答案所示。
另一种选择是通过使用 bang bang bang 运算符取消引号来使用参数拼接 !!!
。此运算符仅在某些 {rlang} 和 {tidyverse} 函数中受支持。在您的示例中,您评估了支持参数拼接的 purrr::map
内的点 ...
。因此可能不需要重写您的函数:
library(tidyverse)
# your original function:
myfun <- function(...) {
ids_lst <- lst(...)
df2 <- bind_rows(map(ids_lst, function(x)
mtcars %>%
filter(gear == x) %>%
select(mpg)), .id = "gear") %>%
left_join(df)
df2
}
myvector <- unique(mtcars$gear)
myfun(!!! myvector) # works
#> Joining, by = "gear"
#> gear mpg id
#> 1 4 21.0 1
#> 2 4 21.0 1
#> 3 4 22.8 1
#> 4 4 24.4 1
#> 5 4 22.8 1
#> 6 4 19.2 1
#> 7 4 17.8 1
#> 8 4 32.4 1
#> 9 4 30.4 1
#> 10 4 33.9 1
#> ...
myfun(3, 4, 5) # works
#> Joining, by = "gear"
#> gear mpg id
#> 1 3 21.4 2
#> 2 3 18.7 2
#> 3 3 18.1 2
#> 4 3 14.3 2
#> 5 3 16.4 2
#> 6 3 17.3 2
#> 7 3 15.2 2
#> 8 3 10.4 2
#> 9 3 10.4 2
#> 10 3 14.7 2
#> ...
由 reprex package (v0.3.0)
于 2021-12-30 创建您可以阅读有关使用 bang bang bang 运算符取消引用的更多信息 here。
最后,您应该考虑您的功能的用户。如果您是唯一的用户,请选择适合您的任何内容。如果有其他用户,您应该考虑他们期望该功能如何工作。用户可能不希望一个函数可以处理多个参数,或者同时在一个向量中提供这些参数。在 tidyverse 参数中,用 !!!
拼接是一个公认的概念。在基础 R 中,我们通常会使用 do.call("myfun", as.list(myvector))
来实现类似的效果。
要添加另一个选项:
purrr 包有一系列 lift
函数,可用于改变函数采用的参数类型。最突出的是 lift_dl
,它将一个以点为参数的函数转换为一个以列表或向量为参数的函数。这也可以用来解决问题而不需要重写函数:
lift_dl(myfun)(myvector)
#> Joining, by = "gear"
#> gear mpg id
#> 1 4 21.0 1
#> 2 4 21.0 1
#> 3 4 22.8 1
#> 4 4 24.4 1
#> 5 4 22.8 1
#> 6 4 19.2 1
#> 7 4 17.8 1
#> 8 4 32.4 1
#> 9 4 30.4 1
#> 10 4 33.9 1
#> ...
由 reprex package (v0.3.0)
创建于 2022-01-01