From h@wickh@m @ending from gm@il@com  Thu May  3 17:34:10 2018
From: h@wickh@m @ending from gm@il@com (Hadley Wickham)
Date: Thu, 3 May 2018 08:34:10 -0700
Subject: [Rd] length of `...`
In-Reply-To: <37141c6a-831b-b74a-7abf-a36d64130f81@gmail.com>
References: <d0b68216-4922-2249-e5e6-1589345dc6d7@kogentum.hu>
 <CAF8bMcaRW88VdyXw-kmCa9dw-awBiNiU+j+nktLRACCj4grPdQ@mail.gmail.com>
 <37141c6a-831b-b74a-7abf-a36d64130f81@gmail.com>
Message-ID: <CABdHhvHzGECDQBsZsXTbakNGW7JuX4tw46_KmcYX8_b3kMB=Fg@mail.gmail.com>

On Thu, May 3, 2018 at 8:18 AM, Duncan Murdoch <murdoch.duncan at gmail.com> wrote:
> On 03/05/2018 11:01 AM, William Dunlap via R-devel wrote:
>>
>> In R-3.5.0 you can use ...length():
>>    > f <- function(..., n) ...length()
>>    > f(stop("one"), stop("two"), stop("three"), n=7)
>>    [1] 3
>>
>> Prior to that substitute() is the way to go
>>    > g <- function(..., n) length(substitute(...()))
>>    > g(stop("one"), stop("two"), stop("three"), n=7)
>>    [1] 3
>>
>> R-3.5.0 also has the ...elt(n) function, which returns
>> the evaluated n'th entry in ... , without evaluating the
>> other ... entries.
>>    > fn <- function(..., n) ...elt(n)
>>    > fn(stop("one"), 3*5, stop("three"), n=2)
>>    [1] 15
>>
>> Prior to 3.5.0, eval the appropriate component of the output
>> of substitute() in the appropriate environment:
>>    > gn <- function(..., n) {
>>    +   nthExpr <- substitute(...())[[n]]
>>    +   eval(nthExpr, envir=parent.frame())
>>    + }
>>    > gn(stop("one"), environment(), stop("two"), n=2)
>>    <environment: R_GlobalEnv>
>>
>
> Bill, the last of these doesn't quite work, because ... can be passed down
> through a string of callers.  You don't necessarily want to evaluate it in
> the parent.frame().  For example:
>
> x <- "global"
> f <- function(...) {
>   x <- "f"
>   g(...)
> }
> g <- function(...) {
>   firstExpr <- substitute(...())[[1]]
>   c(list(...)[[1]], eval(firstExpr, envir = parent.frame()))
> }
>
> Calling g(x) correctly prints "global" twice, but calling f(x) incorrectly
> prints
>
> [1] "global" "f"
>
> You can get the first element of ... without evaluating the rest using ..1,
> but I don't know a way to do this for general n in pre-3.5.0 base R.

If you don't mind using a package:

# works with R 3.1 and up
library(rlang)

x <- "global"
f <- function(...) {
  x <- "f"
  g(...)
}
g <- function(...) {
  dots <- enquos(...)
  eval_tidy(dots[[1]])
}

f(x, stop("!"))
#> [1] "global"
g(x, stop("!"))
#> [1] "global"

Hadley

-- 
http://hadley.nz




[Rd] length of `...`
Hadley Wickham