I might add that there seems to be a subtle difference between using
`...elt()` and `match.call()`, which is that the former causes `a` itself
to be evaluated while the latter doesn't:
```
# Some approaches that have been suggested:
# 1. Using `list()` (Bert Gunter)
f1 <- function(...) list(...)[["a"]]
# 2. Using `...elt()` (Bert Gunter)
f2 <- function(...) ...elt(match("a", ...names()))
# 3. Using argument matching (Hadley Wickham)
f3 <- function(...) (\(a, ...) a)(...)
# 4. Using `match.call()`
f4 <- function(...) eval(match.call()[["a"]], parent.frame())
ff <- list(f1 = f1, f2 = f2, f3 = f3, f4 = f4)
sapply(ff, \(f) {
f(b = 2, a = 1, c = 3)
})
#> f1 f2 f3 f4
#> 1 1 1 1
# View the (defused) arguments after `a` has been accessed:
# returns an expression if the argument has not been evaluated, and a
number if it has
check_forced_args <- function(f) {
body(f) <- call("{", body(f), quote(rlang::enexprs(...)))
# pass `f()` some expressions to see which are evaluated
f(a = cos(0), b = sqrt(4))
}
# make a data frame showing the defused arguments for each function
lapply(ff, check_forced_args) |> do.call(rbind, args = _) |>
as.data.frame()
#> a b
#> f1 1 2 # all the arguments are forced
#> f2 1 sqrt(4) # only `a` is forced
#> f3 1 sqrt(4) # only `a` is forced
#> f4 cos(0) sqrt(4) # none of the arguments are forced
```
Also, here's a possible way to adapt Hadley Wickham's approach so that it
takes the name of the argument as a string, though it does lose the
elegance:
```
pick_arg <- function(nm) {
as.function(c(
setNames(alist(. = , . = ), c(nm, "...")),
as.symbol(nm)
))
}
z <- "a"
f5 <- function(...) {
pick_arg(z)(...)
}
f5(b = 2, a = 1, c = 3)
#> [1] 1
```
Regards,
Ian
____
Ian Farm, Laboratory Manager
University of Maine Agroecology Lab
On Wed, Jan 8, 2025 at 5:58?PM Bert Gunter <bgunter.4567 at gmail.com> wrote:
That's very nice, Hadley. Simple and clean. Never would have thought of it
myself.
As usual, however, in the course of my churnings, I have a further
complication to add. But first ...
**TO ALL**: Feel free to ignore the following, as I'm just fooling around
here and don't want to waste your time with my stupid stuff.
Anyway, the complication is motivated by the use of formals() or otherwise
that *programmatically* generates a character representation of the
arguments I want to select. So, for example:
## Then:
f1 <- function(...){
...elt(match(z, ...names())) ## since z gets evaluated in the call
}
## still works.
[1] 1
But I haven't figured out how to modify your suggestion -- at least in a
simple way -- to do the same. Likely I've missed something, though.
Cheers,
Bert
On Wed, Jan 8, 2025 at 12:51?PM Hadley Wickham <h.wickham at gmail.com>
wrote:
I'd propose an alternative that I think is superior: rely on the
of ... to do the work for you:
f1 <- function(...){
one <- list(...)[['a']]
two <- ...elt(match('a', ...names()))
c(one, two, three(...))
}
three <- function(a, ...) {
a
}
f1(a = 1, b = 2, c = 3)
#> [1] 1 1 1
On Sun, Jan 5, 2025 at 12:00?PM Bert Gunter <bgunter.4567 at gmail.com>
wrote:
Consider:
f1 <- function(...){
one <- list(...)[['a']]
two <- ...elt(match('a', ...names()))
c(one, two)
}
## Here "..." is an argument list with "a" somewhere in it, but in an
unknown position.
[1] 2 2
Which is better for extracting a specific named argument, one<- or
two<- ? Or a third alternative that is better than both?
Comments and critiques welcome.
Cheers,
Bert