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
>>>>> Herv? Pag?s <hpages at fredhutch.org> >>>>> on Thu, 3 May 2018 08:55:20 -0700 writes:> 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. Namely> dotlength <- function(...) nargs()> (This is subtly different from calling nargs() directly as it will > only count the elements in ...)> HadleyWell, I was the "someone". In the past I had seen (and used myself) length(list(...)) and of course that was not usable. I knew of some substitute() / match.call() tricks [but I think did not know Bill's cute substitute(...()) !] at the time, but found them too esoteric. Aditionally and importantly, ...length() and ..elt(n) were developed "synchronously", and the R-substitutes for ..elt() definitely are less trivial (I did not find one at the time), as Duncan's example to Bill's proposal has shown, so I had looked at .Primitive() solutions of both. In hindsight I should have asked here for advice, but may at the time I had been a bit frustrated by the results of some of my RFCs ((nothing specific in mind !)) But __if__ there's really no example where current (3.5.0 and newer) ...length() differs from Hadley's dotlength() I'd vert happy to replace ...length 's C based definition by Hadley's beautiful minimal solution. Martin > 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 > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel
The one difference I see, is the necessity to pass the dots to the function dotlength : dotlength <- function(...) nargs() myfun <- function(..., someArg = 1){ n1 <- ...length() n2 <- dotlength() n3 <- dotlength(...) return(c(n1, n2, n3)) } myfun(stop("A"), stop("B"), someArg = stop("c")) I don't really see immediately how one can replace the C definition with Hadley's solution without changing how the function has to be used. Personally, I have no preference over the use, but changing it now would break code dependent upon ...length() imho. Unless I'm overlooking something of course. Cheers Joris On Fri, May 4, 2018 at 3:02 PM, Martin Maechler <maechler at stat.math.ethz.ch> wrote:> >>>>> Herv? Pag?s <hpages at fredhutch.org> > >>>>> on Thu, 3 May 2018 08:55:20 -0700 writes: > > > 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. > > Namely > > > dotlength <- function(...) nargs() > > > (This is subtly different from calling nargs() directly as it will > > only count the elements in ...) > > > Hadley > > > Well, I was the "someone". In the past I had seen (and used myself) > > length(list(...)) > > and of course that was not usable. > I knew of some substitute() / match.call() tricks [but I think > did not know Bill's cute substitute(...()) !] at the time, but > found them too esoteric. > > Aditionally and importantly, ...length() and ..elt(n) were > developed "synchronously", and the R-substitutes for ..elt() > definitely are less trivial (I did not find one at the time), as > Duncan's example to Bill's proposal has shown, so I had looked > at .Primitive() solutions of both. > > In hindsight I should have asked here for advice, but may at > the time I had been a bit frustrated by the results of some of > my RFCs ((nothing specific in mind !)) > > But __if__ there's really no example where current (3.5.0 and newer) > > ...length() > > differs from Hadley's dotlength() > I'd vert happy to replace ...length 's C based definition by > Hadley's beautiful minimal solution. > > Martin > > > > 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 > > > ______________________________________________ > > R-devel at r-project.org mailing list > > https://stat.ethz.ch/mailman/listinfo/r-devel > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >-- Joris Meys Statistical consultant Department of Data Analysis and Mathematical Modelling Ghent University Coupure Links 653, B-9000 Gent (Belgium) <https://maps.google.com/?q=Coupure+links+653,%C2%A0B-9000+Gent,%C2%A0Belgium&entry=gmail&source=g> ----------- Biowiskundedagen 2017-2018 http://www.biowiskundedagen.ugent.be/ ------------------------------- Disclaimer : http://helpdesk.ugent.be/e-maildisclaimer.php [[alternative HTML version deleted]]
Thanks Martin for the clarifications. H. On 05/04/2018 06:02 AM, Martin Maechler wrote:>>>>>> Herv? Pag?s <hpages at fredhutch.org> >>>>>> on Thu, 3 May 2018 08:55:20 -0700 writes: > > > 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. > > Namely > >> dotlength <- function(...) nargs() > >> (This is subtly different from calling nargs() directly as it will >> only count the elements in ...) > >> Hadley > > > Well, I was the "someone". In the past I had seen (and used myself) > > length(list(...)) > > and of course that was not usable. > I knew of some substitute() / match.call() tricks [but I think > did not know Bill's cute substitute(...()) !] at the time, but > found them too esoteric. > > Aditionally and importantly, ...length() and ..elt(n) were > developed "synchronously", and the R-substitutes for ..elt() > definitely are less trivial (I did not find one at the time), as > Duncan's example to Bill's proposal has shown, so I had looked > at .Primitive() solutions of both. > > In hindsight I should have asked here for advice, but may at > the time I had been a bit frustrated by the results of some of > my RFCs ((nothing specific in mind !)) > > But __if__ there's really no example where current (3.5.0 and newer) > > ...length() > > differs from Hadley's dotlength() > I'd vert happy to replace ...length 's C based definition by > Hadley's beautiful minimal solution. > > Martin > > > > 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 > > > ______________________________________________ > > R-devel at r-project.org mailing list > > https://urldefense.proofpoint.com/v2/url?u=https-3A__stat.ethz.ch_mailman_listinfo_r-2Ddevel&d=DwIDAw&c=eRAMFD45gAfqt84VtBcfhQ&r=BK7q3XeAvimeWdGbWY_wJYbW0WYiZvSXAJJKaaPhzWA&m=BGlRjScM4h5whbLQ891iVxeMRA4PY37vwG3cnC5kuDI&s=_XZhh9pIVv0VBjUYXBgW39dss7YCGQE3XCArLMDfvDo&e>-- 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