Hi, I would like to make a suggestion for a small syntactic modification of FUN argument in the family of functions [lsv]apply(). The idea is to allow one-liner expressions without typing "function(item) {...}" to surround them. The argument to the anonymous function is simply referred as ".". Let take an example. With this new feature, the following call sapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, d))$r.squared) #??????? 4???????? 6???????? 8 #0.5086326 0.4645102 0.4229655 could be rewritten as sapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) "Not a big saving in typing" you can say but multiplied by the number of [lsv]apply usage and a neater look, I think, the idea merits to be considered. To illustrate a possible implementation, I propose a wrapper example for sapply(): wsapply=function(l, fun, ...) { ??? s=substitute(fun) ??? if (is.name(s) || is.call(s) && s[[1]]==as.name("function")) { ??????? sapply(l, fun, ...) # legacy call ??? } else { ??????? sapply(l, function(d) eval(s, list(.=d)), ...) ??? } } Now, we can do: wsapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) or, traditional way: wsapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, d))$r.squared) the both work. How do you feel about that? Best, Serguei.
Serguei,> On 17/04/2020, at 2:24 AM, Sokol Serguei <sokol at insa-toulouse.fr> wrote: > > Hi, > > I would like to make a suggestion for a small syntactic modification of FUN argument in the family of functions [lsv]apply(). The idea is to allow one-liner expressions without typing "function(item) {...}" to surround them. The argument to the anonymous function is simply referred as ".". Let take an example. With this new feature, the following call > > sapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, d))$r.squared) > # 4 6 8 > #0.5086326 0.4645102 0.4229655 > > > could be rewritten as > > sapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) > > "Not a big saving in typing" you can say but multiplied by the number of [lsv]apply usage and a neater look, I think, the idea merits to be considered.It's not in any way "neater", not only is it less readable, it's just plain wrong. What if the expression returned a function? How do you know that you don't want to apply the result of the call? For the same reason the implementation below won't work - very often you just pass a symbol that evaluates to a function and always en expression that returns a function and there is no way to distinguish that from your new proposed syntax. When you feel compelled to use substitute() you should hear alarm bells that something is wrong ;). You can certainly write a new function that uses a different syntax (and I'm sure someone has already done that in the package space), but what you propose is incompatible with *apply in R (and very much not R syntax). Cheers, Simon> To illustrate a possible implementation, I propose a wrapper example for sapply(): > > wsapply=function(l, fun, ...) { > s=substitute(fun) > if (is.name(s) || is.call(s) && s[[1]]==as.name("function")) { > sapply(l, fun, ...) # legacy call > } else { > sapply(l, function(d) eval(s, list(.=d)), ...) > } > } > > Now, we can do: > > wsapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) > > or, traditional way: > > wsapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, d))$r.squared) > > the both work. > > How do you feel about that? > > Best, > Serguei. > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >
Passing in a function passes not only an argument list but also an environment from which to get free variables. Since your function doesn't pay attention to the environment you get things like the following.> wsapply(list(1,2:3), paste(., ":", deparse(s)))[[1]] [1] "1 : paste(., \":\", deparse(s))" [[2]] [1] "2 : paste(., \":\", deparse(s))" "3 : paste(., \":\", deparse(s))" Bill Dunlap TIBCO Software wdunlap tibco.com On Thu, Apr 16, 2020 at 7:25 AM Sokol Serguei <sokol at insa-toulouse.fr> wrote:> Hi, > > I would like to make a suggestion for a small syntactic modification of > FUN argument in the family of functions [lsv]apply(). The idea is to > allow one-liner expressions without typing "function(item) {...}" to > surround them. The argument to the anonymous function is simply referred > as ".". Let take an example. With this new feature, the following call > > sapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, > d))$r.squared) > # 4 6 8 > #0.5086326 0.4645102 0.4229655 > > > could be rewritten as > > sapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) > > "Not a big saving in typing" you can say but multiplied by the number of > [lsv]apply usage and a neater look, I think, the idea merits to be > considered. > To illustrate a possible implementation, I propose a wrapper example for > sapply(): > > wsapply=function(l, fun, ...) { > s=substitute(fun) > if (is.name(s) || is.call(s) && s[[1]]==as.name("function")) { > sapply(l, fun, ...) # legacy call > } else { > sapply(l, function(d) eval(s, list(.=d)), ...) > } > } > > Now, we can do: > > wsapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) > > or, traditional way: > > wsapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, > d))$r.squared) > > the both work. > > How do you feel about that? > > Best, > Serguei. > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >[[alternative HTML version deleted]]
Simon, Thanks for replying. In what follows I won't try to argue (I understood that you find this a bad idea) but I would like to make clearer some of your point for me (and may be for others). Le 16/04/2020 ? 16:48, Simon Urbanek a ?crit?:> Serguei, >> On 17/04/2020, at 2:24 AM, Sokol Serguei <sokol at insa-toulouse.fr> >> wrote: Hi, I would like to make a suggestion for a small syntactic >> modification of FUN argument in the family of functions [lsv]apply(). >> The idea is to allow one-liner expressions without typing >> "function(item) {...}" to surround them. The argument to the >> anonymous function is simply referred as ".". Let take an example. >> With this new feature, the following call sapply(split(mtcars, >> mtcars$cyl), function(d) summary(lm(mpg ~ wt, d))$r.squared) # 4 6 8 >> #0.5086326 0.4645102 0.4229655 could be rewritten as >> sapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) >> "Not a big saving in typing" you can say but multiplied by the number >> of [lsv]apply usage and a neater look, I think, the idea merits to be >> considered. > It's not in any way "neater", not only is it less readable, it's just > plain wrong. What if the expression returned a function?do you mean like in l=sapply(1:3, function(i) function(x) i+x) l[[1]](3) # 4 l[[2]](3) # 5 This is indeed a corner case but a pair of () or {} can keep wsapply() in course: l=wsapply(1:3, (function(x) .+x)) l[[1]](3) # 4 l[[2]](3) # 5> How do you know that you don't want to apply the result of the call?A small example (if it is significantly different from the one above) would be very helpful for me to understand this point.> For the same reason the implementation below won't work - very often > you just pass a symbol that evaluates to a function and always en > expression that returns a function and there is no way to distinguish > that from your new proposed syntax.Even with () or {} around such "dotted" expression? Best, Serguei.> When you feel compelled to use substitute() you should hear alarm > bells that something is wrong ;). You can certainly write a new > function that uses a different syntax (and I'm sure someone has > already done that in the package space), but what you propose is > incompatible with *apply in R (and very much not R syntax). Cheers, Simon >> To illustrate a possible implementation, I propose a wrapper example >> for sapply(): wsapply=function(l, fun, ...) { s=substitute(fun) if >> (is.name(s) || is.call(s) && s[[1]]==as.name("function")) { sapply(l, >> fun, ...) # legacy call } else { sapply(l, function(d) eval(s, >> list(.=d)), ...) } } Now, we can do: wsapply(split(mtcars, >> mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) or, traditional way: >> wsapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, >> d))$r.squared) the both work. How do you feel about that? Best, >> Serguei. ______________________________________________ >> R-devel at r-project.org mailing list >> https://stat.ethz.ch/mailman/listinfo/r-devel >
Thanks Bill, Clearly, my first proposition for wsapply() is quick and dirty one. However, if "." becomes a reserved variable with this new syntax, wsapply() can be fixed (at least for your example and alike) as: wsapply=function(l, fun, ...) { ??? .=substitute(fun) ??? if (is.name(.) || is.call(.) && .[[1]]==as.name("function")) { ??????? sapply(l, fun, ...) ??? } else { ??????? sapply(l, function(d) eval(., list(.=d)), ...) ??? } } Will it do the job? Best, Serguei. Le 16/04/2020 ? 17:07, William Dunlap a ?crit?:> Passing in a function passes not only an argument list but also an > environment from which to get free variables. Since your function > doesn't pay attention to the environment you get things like the > following. > > > wsapply(list(1,2:3), paste(., ":", deparse(s))) > [[1]] > [1] "1 : paste(., \":\", deparse(s))" > > [[2]] > [1] "2 : paste(., \":\", deparse(s))" "3 : paste(., \":\", deparse(s))" > > Bill Dunlap > TIBCO Software > wdunlap tibco.com <http://tibco.com> > > > On Thu, Apr 16, 2020 at 7:25 AM Sokol Serguei <sokol at insa-toulouse.fr > <mailto:sokol at insa-toulouse.fr>> wrote: > > Hi, > > I would like to make a suggestion for a small syntactic > modification of > FUN argument in the family of functions [lsv]apply(). The idea is to > allow one-liner expressions without typing "function(item) {...}" to > surround them. The argument to the anonymous function is simply > referred > as ".". Let take an example. With this new feature, the following call > > sapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, > d))$r.squared) > #??????? 4???????? 6???????? 8 > #0.5086326 0.4645102 0.4229655 > > > could be rewritten as > > sapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) > > "Not a big saving in typing" you can say but multiplied by the > number of > [lsv]apply usage and a neater look, I think, the idea merits to be > considered. > To illustrate a possible implementation, I propose a wrapper > example for > sapply(): > > wsapply=function(l, fun, ...) { > ???? s=substitute(fun) > ???? if (is.name <http://is.name>(s) || is.call(s) && > s[[1]]==as.name <http://as.name>("function")) { > ???????? sapply(l, fun, ...) # legacy call > ???? } else { > ???????? sapply(l, function(d) eval(s, list(.=d)), ...) > ???? } > } > > Now, we can do: > > wsapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) > > or, traditional way: > > wsapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, > d))$r.squared) > > the both work. > > How do you feel about that? > > Best, > Serguei. > > ______________________________________________ > R-devel at r-project.org <mailto:R-devel at r-project.org> mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel >[[alternative HTML version deleted]]
This syntax is already implemented in the {purrr} package, more or less -- you need to add a tilde before your function call for it to work exactly as written: purrr::map_dbl(split(mtcars, mtcars$cyl), ~ summary(lm(wt ~ mpg, .))$r.squared) is equivalent to sapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, d))$r.squared) Seems like using this package is probably an easier solution for this wish than adding a reserved variable and adding additional syntax to the apply family as a whole. Thanks, -Mike> From: Sokol Serguei <sokol at insa-toulouse.fr> > Date: Thu, Apr 16, 2020 at 12:03 PM > Subject: Re: [Rd] suggestion: "." in [lsv]apply() > To: William Dunlap <wdunlap at tibco.com> > Cc: r-devel <r-devel at r-project.org> > > > Thanks Bill, > > Clearly, my first proposition for wsapply() is quick and dirty one. > However, if "." becomes a reserved variable with this new syntax, > wsapply() can be fixed (at least for your example and alike) as: > > wsapply=function(l, fun, ...) { > .=substitute(fun) > if (is.name(.) || is.call(.) && .[[1]]==as.name("function")) { > sapply(l, fun, ...) > } else { > sapply(l, function(d) eval(., list(.=d)), ...) > } > } > > Will it do the job? > > Best, > Serguei. > > Le 16/04/2020 ? 17:07, William Dunlap a ?crit : > > Passing in a function passes not only an argument list but also an > > environment from which to get free variables. Since your function > > doesn't pay attention to the environment you get things like the > > following. > > > > > wsapply(list(1,2:3), paste(., ":", deparse(s))) > > [[1]] > > [1] "1 : paste(., \":\", deparse(s))" > > > > [[2]] > > [1] "2 : paste(., \":\", deparse(s))" "3 : paste(., \":\", deparse(s))" > > > > Bill Dunlap > > TIBCO Software > > wdunlap tibco.com <http://tibco.com> > > > > > > On Thu, Apr 16, 2020 at 7:25 AM Sokol Serguei <sokol at insa-toulouse.fr > > <mailto:sokol at insa-toulouse.fr>> wrote: > > > > Hi, > > > > I would like to make a suggestion for a small syntactic > > modification of > > FUN argument in the family of functions [lsv]apply(). The idea is to > > allow one-liner expressions without typing "function(item) {...}" to > > surround them. The argument to the anonymous function is simply > > referred > > as ".". Let take an example. With this new feature, the following call > > > > sapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, > > d))$r.squared) > > # 4 6 8 > > #0.5086326 0.4645102 0.4229655 > > > > > > could be rewritten as > > > > sapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) > > > > "Not a big saving in typing" you can say but multiplied by the > > number of > > [lsv]apply usage and a neater look, I think, the idea merits to be > > considered. > > To illustrate a possible implementation, I propose a wrapper > > example for > > sapply(): > > > > wsapply=function(l, fun, ...) { > > s=substitute(fun) > > if (is.name <http://is.name>(s) || is.call(s) && > > s[[1]]==as.name <http://as.name>("function")) { > > sapply(l, fun, ...) # legacy call > > } else { > > sapply(l, function(d) eval(s, list(.=d)), ...) > > } > > } > > > > Now, we can do: > > > > wsapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) > > > > or, traditional way: > > > > wsapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, > > d))$r.squared) > > > > the both work. > > > > How do you feel about that? > > > > Best, > > Serguei. > > > > ______________________________________________ > > R-devel at r-project.org <mailto:R-devel at r-project.org> mailing list > > https://stat.ethz.ch/mailman/listinfo/r-devel > > > > > [[alternative HTML version deleted]] > > ______________________________________________ > R-devel at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel
I'm sure this exists elsewhere, but, as a trade-off, could you achieve what you want with a separate helper function F(expr) that constructs the function you want to pass to [lsv]apply()? Something that would allow you to write: sapply(split(mtcars, mtcars$cyl), F(summary(lm(mpg ~ wt,.))$r.squared)) Such an F() function would apply elsewhere too. /Henrik On Thu, Apr 16, 2020 at 9:30 AM Michael Mahoney <mike.mahoney.218 at gmail.com> wrote:> > This syntax is already implemented in the {purrr} package, more or > less -- you need to add a tilde before your function call for it to > work exactly as written: > > purrr::map_dbl(split(mtcars, mtcars$cyl), ~ summary(lm(wt ~ mpg, .))$r.squared) > > is equivalent to > > sapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, > d))$r.squared) > > Seems like using this package is probably an easier solution for this > wish than adding a reserved variable and adding additional syntax to > the apply family as a whole. > > Thanks, > > -Mike > > > From: Sokol Serguei <sokol at insa-toulouse.fr> > > Date: Thu, Apr 16, 2020 at 12:03 PM > > Subject: Re: [Rd] suggestion: "." in [lsv]apply() > > To: William Dunlap <wdunlap at tibco.com> > > Cc: r-devel <r-devel at r-project.org> > > > > > > Thanks Bill, > > > > Clearly, my first proposition for wsapply() is quick and dirty one. > > However, if "." becomes a reserved variable with this new syntax, > > wsapply() can be fixed (at least for your example and alike) as: > > > > wsapply=function(l, fun, ...) { > > .=substitute(fun) > > if (is.name(.) || is.call(.) && .[[1]]==as.name("function")) { > > sapply(l, fun, ...) > > } else { > > sapply(l, function(d) eval(., list(.=d)), ...) > > } > > } > > > > Will it do the job? > > > > Best, > > Serguei. > > > > Le 16/04/2020 ? 17:07, William Dunlap a ?crit : > > > Passing in a function passes not only an argument list but also an > > > environment from which to get free variables. Since your function > > > doesn't pay attention to the environment you get things like the > > > following. > > > > > > > wsapply(list(1,2:3), paste(., ":", deparse(s))) > > > [[1]] > > > [1] "1 : paste(., \":\", deparse(s))" > > > > > > [[2]] > > > [1] "2 : paste(., \":\", deparse(s))" "3 : paste(., \":\", deparse(s))" > > > > > > Bill Dunlap > > > TIBCO Software > > > wdunlap tibco.com <http://tibco.com> > > > > > > > > > On Thu, Apr 16, 2020 at 7:25 AM Sokol Serguei <sokol at insa-toulouse.fr > > > <mailto:sokol at insa-toulouse.fr>> wrote: > > > > > > Hi, > > > > > > I would like to make a suggestion for a small syntactic > > > modification of > > > FUN argument in the family of functions [lsv]apply(). The idea is to > > > allow one-liner expressions without typing "function(item) {...}" to > > > surround them. The argument to the anonymous function is simply > > > referred > > > as ".". Let take an example. With this new feature, the following call > > > > > > sapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, > > > d))$r.squared) > > > # 4 6 8 > > > #0.5086326 0.4645102 0.4229655 > > > > > > > > > could be rewritten as > > > > > > sapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) > > > > > > "Not a big saving in typing" you can say but multiplied by the > > > number of > > > [lsv]apply usage and a neater look, I think, the idea merits to be > > > considered. > > > To illustrate a possible implementation, I propose a wrapper > > > example for > > > sapply(): > > > > > > wsapply=function(l, fun, ...) { > > > s=substitute(fun) > > > if (is.name <http://is.name>(s) || is.call(s) && > > > s[[1]]==as.name <http://as.name>("function")) { > > > sapply(l, fun, ...) # legacy call > > > } else { > > > sapply(l, function(d) eval(s, list(.=d)), ...) > > > } > > > } > > > > > > Now, we can do: > > > > > > wsapply(split(mtcars, mtcars$cyl), summary(lm(mpg ~ wt, .))$r.squared) > > > > > > or, traditional way: > > > > > > wsapply(split(mtcars, mtcars$cyl), function(d) summary(lm(mpg ~ wt, > > > d))$r.squared) > > > > > > the both work. > > > > > > How do you feel about that? > > > > > > Best, > > > Serguei. > > > > > > ______________________________________________ > > > R-devel at r-project.org <mailto:R-devel at r-project.org> mailing list > > > https://stat.ethz.ch/mailman/listinfo/r-devel > > > > > > > > > [[alternative HTML version deleted]] > > > > ______________________________________________ > > 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