Reputation: 3175
I thought I had understood how to use the additional arguments argument (...
) of purrr::map
. Here is some code that hopefully illustrates the (to me) unexpected behaviour of purrr::map
:
It seems that passing argument a
as additional argument in purrr::map
is not working:
library(purrr)
f <- function(a, b) {
a + b
}
g <- function(a = 0, b) {
a + b
}
map(1:3, .f = ~ f(b = .x, a = 1))
#> [[1]]
#> [1] 2
#>
#> [[2]]
#> [1] 3
#>
#> [[3]]
#> [1] 4
map(1:3, .f = ~ f(b = .x), a = 1)
#> Error in f(b = .x): argument "a" is missing, with no default
map(1:3, .f = ~ g(b = .x, a = 1))
#> [[1]]
#> [1] 2
#>
#> [[2]]
#> [1] 3
#>
#> [[3]]
#> [1] 4
map(1:3, .f = ~ g(b = .x), a = 1)
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 2
#>
#> [[3]]
#> [1] 3
lapply(1:3, function(b, a = 1) f(a, b))
#> [[1]]
#> [1] 2
#>
#> [[2]]
#> [1] 3
#>
#> [[3]]
#> [1] 4
lapply(1:3, function(b, a) f(a, b), a = 1)
#> [[1]]
#> [1] 2
#>
#> [[2]]
#> [1] 3
#>
#> [[3]]
#> [1] 4
My question is why does the code:
map(1:3, .f = ~ f(b = .x), a = 1)
throw an error?
Upvotes: 2
Views: 778
Reputation: 13731
Behind the scenes, map()
calls as_mapper(). We can do this by hand to see what's going on:
purrr::as_mapper( ~ f(b = .x, a = 1) )
# <lambda>
# function (..., .x = ..1, .y = ..2, . = ..1)
# f(b = .x, a = 1) <----
# attr(,"class")
# [1] "rlang_lambda_function" "function"
purrr::as_mapper( ~ f(b = .x), a=1 )
# <lambda>
# function (..., .x = ..1, .y = ..2, . = ..1)
# f(b = .x) <----
# attr(,"class")
# [1] "rlang_lambda_function" "function"
I highlighted the important distinction with <---
. Notice that in the second case, the lambda function that gets created does not incorporate your extra a=1
parameter, which leads to the error you are observing.
To address your comment, a=1
actually is being passed to the lambda function. Your lambda function just isn't doing anything with it. To properly incorporate a
, the lambda function definition needs to handle the ...
dots:
g <- function(a, b, ...) {a + b} # ... are needed to catch all extra
# arguments from as_mapper
purrr::as_mapper( .f = ~ g(b=.x, ...) )
# <lambda>
# function (..., .x = ..1, .y = ..2, . = ..1)
# g(b = .x, ...) <-- dots are now forwarded to g()
# attr(,"class")
# [1] "rlang_lambda_function" "function"
purrr::map(1:3, .f = ~ g(b=.x, ...), a=1 ) # a now properly gets passed to g
# [[1]]
# [1] 2
#
# [[2]]
# [1] 3
#
# [[3]]
# [1] 4
Upvotes: 2
Reputation: 887851
We could pass the remaining arguments without any anonymous function
library(purrr)
map(1:3, f, a = 1)
#[[1]]
#[1] 2
#[[2]]
#[1] 3
#[[3]]
#[1] 4
Or another option is rlang::as_function
or purrr:as_mapper
map(1:3, as_mapper(f), a = 1)
Or create the f
on the fly
map(1:3, as_mapper(~ .x + .y), a = 1)
Or call it in invoke
map(1:3, ~ invoke(f, b = .x, a = 1))
#[[1]]
#[1] 2
#[[2]]
#[1] 3
#[[3]]
#[1] 4
This would make it more easier to read than the .f = ~ f(b = .x), a = 1
Upvotes: 2