Peter Meilstrup
2018-Aug-13 05:09 UTC
[Rd] substitute() on arguments in ellipsis ("dot dot dot")?
Interestingly, as.list(substitute(...())) also works. On Sun, Aug 12, 2018 at 1:16 PM, Duncan Murdoch <murdoch.duncan at gmail.com> wrote:> On 12/08/2018 4:00 PM, Henrik Bengtsson wrote: >> >> Hi. For any number of *known* arguments, we can do: >> >> one <- function(a) list(a = substitute(a)) >> two <- function(a, b) list(a = substitute(a), b = substitute(b)) >> >> and so on. But how do I achieve the same when I have: >> >> dots <- function(...) list(???) >> >> I want to implement this such that I can do: >> >>> exprs <- dots(1+2) >>> str(exprs) >> >> List of 1 >> $ : language 1 + 2 >> >> as well as: >> >>> exprs <- dots(1+2, "a", rnorm(3)) >>> str(exprs) >> >> List of 3 >> $ : language 1 + 2 >> $ : chr "a" >> $ : language rnorm(3) >> >> Is this possible to achieve using plain R code? > > > I think so. substitute(list(...)) gives you a single expression containing > a call to list() with the unevaluated arguments; you can convert that to > what you want using something like > > dots <- function (...) { > exprs <- substitute(list(...)) > as.list(exprs[-1]) > } > > Duncan Murdoch > > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel
Henrik Bengtsson
2018-Aug-13 09:18 UTC
[Rd] substitute() on arguments in ellipsis ("dot dot dot")?
Thanks all, this was very helpful. Peter's finding - dots2() below -
is indeed interesting - I'd be curious to learn what goes on there.
The different alternatives perform approximately the same;
dots1 <- function(...) as.list(substitute(list(...)))[-1L]
dots2 <- function(...) as.list(substitute(...()))
dots3 <- function(...) match.call(expand.dots = FALSE)[["..."]]
stats <- microbenchmark::microbenchmark(
dots1(1+2, "a", rnorm(3), stop("bang!")),
dots2(1+2, "a", rnorm(3), stop("bang!")),
dots3(1+2, "a", rnorm(3), stop("bang!")),
times = 10e3
)
print(stats)
# Unit: microseconds
# expr min lq mean median
uq max neval
# dots1(1 + 2, "a", rnorm(3), stop("bang!")) 2.14 2.45 3.04
2.58
2.73 1110 10000
# dots2(1 + 2, "a", rnorm(3), stop("bang!")) 1.81 2.10 2.47
2.21
2.34 1626 10000
# dots3(1 + 2, "a", rnorm(3), stop("bang!")) 2.59 2.98 3.36
3.15
3.31 1037 10000
/Henrik
On Mon, Aug 13, 2018 at 7:10 AM Peter Meilstrup
<peter.meilstrup at gmail.com> wrote:>
> Interestingly,
>
> as.list(substitute(...()))
>
> also works.
>
> On Sun, Aug 12, 2018 at 1:16 PM, Duncan Murdoch
> <murdoch.duncan at gmail.com> wrote:
> > On 12/08/2018 4:00 PM, Henrik Bengtsson wrote:
> >>
> >> Hi. For any number of *known* arguments, we can do:
> >>
> >> one <- function(a) list(a = substitute(a))
> >> two <- function(a, b) list(a = substitute(a), b =
substitute(b))
> >>
> >> and so on. But how do I achieve the same when I have:
> >>
> >> dots <- function(...) list(???)
> >>
> >> I want to implement this such that I can do:
> >>
> >>> exprs <- dots(1+2)
> >>> str(exprs)
> >>
> >> List of 1
> >> $ : language 1 + 2
> >>
> >> as well as:
> >>
> >>> exprs <- dots(1+2, "a", rnorm(3))
> >>> str(exprs)
> >>
> >> List of 3
> >> $ : language 1 + 2
> >> $ : chr "a"
> >> $ : language rnorm(3)
> >>
> >> Is this possible to achieve using plain R code?
> >
> >
> > I think so. substitute(list(...)) gives you a single expression
containing
> > a call to list() with the unevaluated arguments; you can convert that
to
> > what you want using something like
> >
> > dots <- function (...) {
> > exprs <- substitute(list(...))
> > as.list(exprs[-1])
> > }
> >
> > Duncan Murdoch
> >
> >
> > ______________________________________________
> > R-devel at r-project.org mailing list
> > https://stat.ethz.ch/mailman/listinfo/r-devel
Hadley Wickham
2018-Aug-13 23:58 UTC
[Rd] substitute() on arguments in ellipsis ("dot dot dot")?
Since you're already using bang-bang ;)
library(rlang)
dots1 <- function(...) as.list(substitute(list(...)))[-1L]
dots2 <- function(...) as.list(substitute(...()))
dots3 <- function(...) match.call(expand.dots = FALSE)[["..."]]
dots4 <- function(...) exprs(...)
bench::mark(
dots1(1+2, "a", rnorm(3), stop("bang!")),
dots2(1+2, "a", rnorm(3), stop("bang!")),
dots3(1+2, "a", rnorm(3), stop("bang!")),
dots4(1+2, "a", rnorm(3), stop("bang!")),
check = FALSE
)[1:4]
#> # A tibble: 4 x 4
#> expression min mean median
#> <chr> <bch:tm>
<bch:tm> <bch:t>
#> 1 "dots1(1 + 2, \"a\", rnorm(3), stop(\"bang!\"?
3.23?s 4.15?s 3.81?s
#> 2 "dots2(1 + 2, \"a\", rnorm(3), stop(\"bang!\"?
2.72?s 4.48?s 3.37?s
#> 3 "dots3(1 + 2, \"a\", rnorm(3), stop(\"bang!\"?
4.06?s 4.94?s 4.69?s
#> 4 "dots4(1 + 2, \"a\", rnorm(3), stop(\"bang!\"?
3.92?s 4.9?s 4.46?s
On Mon, Aug 13, 2018 at 4:19 AM Henrik Bengtsson
<henrik.bengtsson at gmail.com> wrote:>
> Thanks all, this was very helpful. Peter's finding - dots2() below -
> is indeed interesting - I'd be curious to learn what goes on there.
>
> The different alternatives perform approximately the same;
>
> dots1 <- function(...) as.list(substitute(list(...)))[-1L]
> dots2 <- function(...) as.list(substitute(...()))
> dots3 <- function(...) match.call(expand.dots =
FALSE)[["..."]]
>
> stats <- microbenchmark::microbenchmark(
> dots1(1+2, "a", rnorm(3), stop("bang!")),
> dots2(1+2, "a", rnorm(3), stop("bang!")),
> dots3(1+2, "a", rnorm(3), stop("bang!")),
> times = 10e3
> )
> print(stats)
> # Unit: microseconds
> # expr min lq mean median
> uq max neval
> # dots1(1 + 2, "a", rnorm(3), stop("bang!")) 2.14 2.45
3.04 2.58
> 2.73 1110 10000
> # dots2(1 + 2, "a", rnorm(3), stop("bang!")) 1.81 2.10
2.47 2.21
> 2.34 1626 10000
> # dots3(1 + 2, "a", rnorm(3), stop("bang!")) 2.59 2.98
3.36 3.15
> 3.31 1037 10000
>
> /Henrik
>
> On Mon, Aug 13, 2018 at 7:10 AM Peter Meilstrup
> <peter.meilstrup at gmail.com> wrote:
> >
> > Interestingly,
> >
> > as.list(substitute(...()))
> >
> > also works.
> >
> > On Sun, Aug 12, 2018 at 1:16 PM, Duncan Murdoch
> > <murdoch.duncan at gmail.com> wrote:
> > > On 12/08/2018 4:00 PM, Henrik Bengtsson wrote:
> > >>
> > >> Hi. For any number of *known* arguments, we can do:
> > >>
> > >> one <- function(a) list(a = substitute(a))
> > >> two <- function(a, b) list(a = substitute(a), b =
substitute(b))
> > >>
> > >> and so on. But how do I achieve the same when I have:
> > >>
> > >> dots <- function(...) list(???)
> > >>
> > >> I want to implement this such that I can do:
> > >>
> > >>> exprs <- dots(1+2)
> > >>> str(exprs)
> > >>
> > >> List of 1
> > >> $ : language 1 + 2
> > >>
> > >> as well as:
> > >>
> > >>> exprs <- dots(1+2, "a", rnorm(3))
> > >>> str(exprs)
> > >>
> > >> List of 3
> > >> $ : language 1 + 2
> > >> $ : chr "a"
> > >> $ : language rnorm(3)
> > >>
> > >> Is this possible to achieve using plain R code?
> > >
> > >
> > > I think so. substitute(list(...)) gives you a single expression
containing
> > > a call to list() with the unevaluated arguments; you can convert
that to
> > > what you want using something like
> > >
> > > dots <- function (...) {
> > > exprs <- substitute(list(...))
> > > as.list(exprs[-1])
> > > }
> > >
> > > Duncan Murdoch
> > >
> > >
> > > ______________________________________________
> > > 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
--
http://hadley.nz