substitute() on arguments in ellipsis ("dot dot dot")?

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

substitute() on arguments in ellipsis ("dot dot dot")?

Henrik Bengtsson-5
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?

Thanks,

Henrik

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: substitute() on arguments in ellipsis ("dot dot dot")?

Duncan Murdoch-2
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

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: substitute() on arguments in ellipsis ("dot dot dot")?

Jeroen Ooms
In reply to this post by Henrik Bengtsson-5
On Sun, Aug 12, 2018 at 10:00 PM, Henrik Bengtsson
<[hidden email]> 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?

You could use match.call, for example:

  dots <- function(...) match.call(expand.dots = FALSE)[['...']]

Note that this returns a pairlist, so if you want an ordinary list you
should wrap it in as.list()

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: substitute() on arguments in ellipsis ("dot dot dot")?

Peter Meilstrup
In reply to this post by Duncan Murdoch-2
Interestingly,

   as.list(substitute(...()))

also works.

On Sun, Aug 12, 2018 at 1:16 PM, Duncan Murdoch
<[hidden email]> 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
>
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: substitute() on arguments in ellipsis ("dot dot dot")?

Henrik Bengtsson-5
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
<[hidden email]> wrote:

>
> Interestingly,
>
>    as.list(substitute(...()))
>
> also works.
>
> On Sun, Aug 12, 2018 at 1:16 PM, Duncan Murdoch
> <[hidden email]> 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
> >
> >
> > ______________________________________________
> > [hidden email] mailing list
> > https://stat.ethz.ch/mailman/listinfo/r-devel

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: substitute() on arguments in ellipsis ("dot dot dot")?

hadley wickham
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
<[hidden email]> 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
> <[hidden email]> wrote:
> >
> > Interestingly,
> >
> >    as.list(substitute(...()))
> >
> > also works.
> >
> > On Sun, Aug 12, 2018 at 1:16 PM, Duncan Murdoch
> > <[hidden email]> 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
> > >
> > >
> > > ______________________________________________
> > > [hidden email] mailing list
> > > https://stat.ethz.ch/mailman/listinfo/r-devel
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel



--
http://hadley.nz

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: substitute() on arguments in ellipsis ("dot dot dot")?

R devel mailing list
In reply to this post by Henrik Bengtsson-5
A potential solution, at least in terms of producing the desired output, is the base R function alist:
> alist(1+2, "a", rnorm(3))
[[1]]
1 + 2

[[2]]
[1] "a"

[[3]]
rnorm(3)

> str(alist(1+2, "a", rnorm(3)))
List of 3
 $ : language 1 + 2
 $ : chr "a"
 $ : language rnorm(3)


luke

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: substitute() on arguments in ellipsis ("dot dot dot")?

Jim Hester
In reply to this post by hadley wickham
Assuming you are fine with a pairlist instead of a list avoiding the
`as.list()` call for dots2 saves a reasonable amount of time and makes it
clearly the fastest.

library(rlang)

dots1 <- function(...) as.list(substitute(list(...)))[-1L]
dots2 <- function(...) as.list(substitute(...()))
dots2.5 <- function(...) 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!")),
  dots2.5(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: 5 x 4
#>   expression                                         min     mean
 median
#>   <chr>                                         <bch:tm> <bch:tm>
<bch:tm>
#> 1 "dots1(1 + 2, \"a\", rnorm(3), stop(\"bang!\…   2.38µs   5.63µs
 2.89µs
#> 2 "dots2(1 + 2, \"a\", rnorm(3), stop(\"bang!\…   2.07µs    3.1µs
2.6µs
#> 3 "dots2.5(1 + 2, \"a\", rnorm(3), stop(\"bang…    471ns  789.5ns
638ns
#> 4 "dots3(1 + 2, \"a\", rnorm(3), stop(\"bang!\…   3.17µs   4.83µs
 4.22µs
#> 5 "dots4(1 + 2, \"a\", rnorm(3), stop(\"bang!\…   3.16µs   4.43µs
 3.87µs


On Mon, Aug 13, 2018 at 7:59 PM Hadley Wickham <[hidden email]> wrote:

> 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
> <[hidden email]> 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
> > <[hidden email]> wrote:
> > >
> > > Interestingly,
> > >
> > >    as.list(substitute(...()))
> > >
> > > also works.
> > >
> > > On Sun, Aug 12, 2018 at 1:16 PM, Duncan Murdoch
> > > <[hidden email]> 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
> > > >
> > > >
> > > > ______________________________________________
> > > > [hidden email] mailing list
> > > > https://stat.ethz.ch/mailman/listinfo/r-devel
> >
> > ______________________________________________
> > [hidden email] mailing list
> > https://stat.ethz.ch/mailman/listinfo/r-devel
>
>
>
> --
> http://hadley.nz
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>

        [[alternative HTML version deleted]]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel