[R] Curry with `[.function` ?

Gabor Grothendieck ggrothendieck at gmail.com
Mon Mar 21 17:44:23 CET 2011


On Mon, Mar 21, 2011 at 12:20 PM, Kenn Konstabel <lebatsnok at gmail.com> wrote:
> On Mon, Mar 21, 2011 at 2:53 PM, Gabor Grothendieck
> <ggrothendieck at gmail.com> wrote:
>>
>> On Mon, Mar 21, 2011 at 8:46 AM, Kenn Konstabel <lebatsnok at gmail.com>
>> wrote:
>> > Dear all,
>> >
>> > I sometimes use the following function:
>> >
>> > Curry <- function(FUN,...) {
>> >   # by Byron Ellis,
>> > https://stat.ethz.ch/pipermail/r-devel/2007-November/047318.html
>> >   .orig <- list(...)
>> >   function(...) do.call(FUN,c(.orig,list(...)))
>> >   }
>> >
>> > ... and have thought it might be convenient to have a method for [ doing
>> > this. As a simple example,
>> >
>> >> apply(M, 1, mean[trim=0.1])  # hypothetical equivalent to apply(M, 1,
>> > Curry(mean, trim=0.1))
>> >
>> >  would be easier to understand  than passing arguments by ...
>> >
>> >> apply(M, 1, mean, trim=0.1)
>> >
>> > and much shorter than using an anonymous function
>> >
>> >> apply(M, 1, function(x) mean(x, trim=0.1)
>> >
>> > This would be much more useful for complicated functions that may take
>> > several functions as arguments. For example (my real examples are too
>> > long
>> > but this seems general enough),
>> >
>> > foo <- function(x, ...) {
>> >     dots <- list(...)
>> >     mapply(function(f) f(x), dots)
>> >     }
>> >
>> > foo(1:10, mean, sd)
>> > foo(c(1:10, NA), mean, mean[trim=0.1, na.rm=TRUE], sd[na.rm=TRUE])
>> >
>> > Defining `[.function` <- Curry won't help:
>> >
>> >> mean[trim=0.1]
>> > Error in mean[trim = 0.1] : object of type 'closure' is not subsettable
>> >
>> > One can write summary and other methods for class "function" without
>> > such
>> > problems, so this has something to do with [ being a primitive function
>> > and
>> > not using UseMethod, it would be foolish to re-define it as an
>> > "ordinary"
>> > generic function.
>> >
>> > Redefining mean as structure(mean, class="function") will make it work
>> > but
>> > then one would have to do it for all functions which is not feasible.
>> >
>> >> class(mean) <- class(mean)
>> >> class(sd)<-class(sd)
>> >> foo(c(1:10, NA), mean, mean[na.rm=TRUE], mean[trim=0.1, na.rm=TRUE],
>> > sd[na.rm=TRUE])
>> > [1]       NA 5.500000 5.500000 3.027650
>> >
>> > Or one could define a short-named function (say, .) doing this:
>> >
>> >> rm(mean, sd) ## removing the modified copies from global environment
>> >> .<-function(x) structure(x, class=class(x))
>> >> foo(c(1:10, NA), mean, .(mean)[na.rm=TRUE], .(mean)[trim=0.1,
>> >> na.rm=TRUE],
>> > .(sd)[na.rm=TRUE])
>> >
>> > But this is not as nice. (And neither is replacing "[" with "Curry" by
>> > using
>> > substitute et al. inside `foo`, - this would make it usable only within
>> > functions that one could be bothered to redefine this way - probably
>> > none.)
>> >
>> > Thanks in advance for any ideas and comments (including the ones saying
>> > that
>> > this is an awful idea)
>>
>> If the aim is to find some short form to express functions then
>> gsubfn's fn$ construct provides a way. It allows you to specify
>> functions using formula notation.  The left hand side of the formula
>> is the arguments and the right hand side is the body.  If no args are
>> specified then it uses the free variables in the body in the order
>> encountered to form the args.  Thus one could write the following.
>> (Since no args were specified and the right hand side uses the free
>> variable x it assumes that there is a single arg x.)
>>
>> library(gsubfn)
>> fn$apply(longley, 2, ~ mean(x, trim = 0.1))
>> fn$lapply(longley, ~ mean(x, trim = 0.1))
>> fn$sapply(longley, ~ mean(x, trim = 0.1))
>>
>> fn$ can preface just about any function.  It does not have to be one
>> of the above.  See ?fn and http://gsubfn.googlecode.com for more info.
>>
>
> Thanks a lot! This is not exactly what I meant but it's a very useful hint.
> The idea was to find a concise way of using function with different default
> values for some of the arguments or to "fix" some of the arguments so that
> the result is a unary function.
>
> I couldn't find a way to do it with gsubfn package directly (although maybe
> I should have another look) but by stealing some of your ideas:
>
> . <- structure(NA, class="rice")
> `$.rice` <- function(x,y) structure(match.fun(y), class="function")
> `[.function` <- function(FUN,...) {
>    # https://stat.ethz.ch/pipermail/r-devel/2007-November/047318.html
>    .orig <- list(...)
>    function(...) do.call(FUN,c(.orig,list(...)))
>    }
>
> .$mean[trim=0.1](c(0:99,1e+20))
> # 50
>

Using gsubfn's fn$ the trick is to  use the identity function:

  library(gsubfn)
  mean.trim.1 <- fn$identity(~ mean(x, trim = 0.1))

Now
  mean.trim.1(x)
is the same as
  mean(x, trim = 0.1)

or in one line:

fn$identity(~ mean(x, trim = 0.1))(c(.99, 1e20))

-- 
Statistics & Software Consulting
GKX Group, GKX Associates Inc.
tel: 1-877-GKX-GROUP
email: ggrothendieck at gmail.com



More information about the R-help mailing list