From hp@ge@ @ending from fredhutch@org  Thu May  3 17:55:20 2018
From: hp@ge@ @ending from fredhutch@org (=?UTF-8?B?SGVydsOpIFBhZ8Oocw==?=)
Date: Thu, 3 May 2018 08:55:20 -0700
Subject: [Rd] length of `...`
In-Reply-To: <CABdHhvHzGECDQBsZsXTbakNGW7JuX4tw46_KmcYX8_b3kMB=Fg@mail.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>
 <CABdHhvHzGECDQBsZsXTbakNGW7JuX4tw46_KmcYX8_b3kMB=Fg@mail.gmail.com>
Message-ID: <3ffcd05b-6417-87be-94e0-f621c4a3116b@fredhutch.org>

Hi,

It would be great if one of the experts could comment on the
difference between Hadley's dotlength and ...length? The fact
that someone bothered to implement a new primitive for that
when there seems to be a very simple and straightforward R-only
solution suggests that there might be some gotchas/pitfalls with
the R-only solution.

Thanks,
H.


On 05/03/2018 08:34 AM, Hadley Wickham wrote:
> 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
> 

-- 
Herv? Pag?s

Program in Computational Biology
Division of Public Health Sciences
Fred Hutchinson Cancer Research Center
1100 Fairview Ave. N, M1-B514
P.O. Box 19024
Seattle, WA 98109-1024

E-mail: hpages at fredhutch.org
Phone:  (206) 667-5791
Fax:    (206) 667-1319




[Rd] length of `...`
=?UTF-8?B?SGVydsOpIFBhZ8Oocw==?=