[R] Extracting specific arguments from "..."

Bert Gunter bgunter@4567 @end|ng |rom gm@||@com
Thu Jan 9 21:59:07 CET 2025


Thanks Ian (and others),

Taking the advice of ?"..." (which I should have done at the outset ...
duhh!),  the following seems to be the simplest solution, adequate for my
needs at least:

f <- function(x, ...){
   lapply(seq_along(x), \(i)switch(x[i], ..., NA))
}

(the usual argument checking etc. should be added, of course.)

Comparing this to the ...xx() functions that I previously gave, which are
the same as names(list(...)) etc. without the overhead of unnecessary
evaluation, gives:

g <- function(...){
   one <- f(z,...)
   two <- lapply(charmatch(z, ...names()), \(i)...elt(i))
  list(one = one, two = two)
}

> k <- 5
> z <- c("a","c")
> g(b=2, a=1, c = k)
$one
$one[[1]]
[1] 1

$one[[2]]
[1] 5


$two
$two[[1]]
[1] 1

$two[[2]]
[1] 5

As always, corrections and comments welcomed.

Cheers,
Bert



On Thu, Jan 9, 2025 at 5:12 AM Ian Farm <ian.farm using maine.edu> wrote:

> 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 using 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:
>>
>> > z <- "a"
>> ## Then:
>> f1 <- function(...){
>>     ...elt(match(z, ...names())) ## since z gets evaluated in the call
>> }
>> ## still works.
>> > f1(b =2, a=1, c=3)
>> [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 using gmail.com>
>> wrote:
>>
>> > I'd propose an alternative that I think is superior: rely on the
>> semantics
>> > 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 using 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.
>> >>
>> >> > f1(b=5, a = 2, c=7)
>> >> [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
>> >>
>> >> ______________________________________________
>> >> R-help using r-project.org mailing list -- To UNSUBSCRIBE and more, see
>> >> https://stat.ethz.ch/mailman/listinfo/r-help
>> >> PLEASE do read the posting guide
>> >> https://www.R-project.org/posting-guide.html
>> >> and provide commented, minimal, self-contained, reproducible code.
>> >>
>> >
>> >
>> > --
>> > http://hadley.nz
>> >
>>
>>         [[alternative HTML version deleted]]
>>
>> ______________________________________________
>> R-help using r-project.org mailing list -- To UNSUBSCRIBE and more, see
>> https://stat.ethz.ch/mailman/listinfo/r-help
>> PLEASE do read the posting guide
>> https://www.R-project.org/posting-guide.html
>> and provide commented, minimal, self-contained, reproducible code.
>>
>

	[[alternative HTML version deleted]]



More information about the R-help mailing list