With which generality can tilde operator be used to build anonymous functions?

我的未来我决定 提交于 2021-01-29 11:19:08

问题


Inside some functions as dplyr::mutate_at or purrr::map, it seems that one can use tilde operator ~ to build anonymous functions.

For example, one can do as in the linked question: map(iris, ~length(unique(.)))

Or also: mtcars %>% mutate_all(~.*2)

I tried to imitate this inside sapply, to avoid sapply(list, function(item) {something_with_item}). I was writing sapply(list, ~ something_with_.) but I get an error

Error in match.fun(FUN) : 
  '~ something_with_.' is not a function, character or symbol

A reproducible example:

> sapply(names(mtcars),function(item) mean(mtcars[[item]]))
       mpg        cyl       disp         hp       drat         wt       qsec 
 20.090625   6.187500 230.721875 146.687500   3.596563   3.217250  17.848750 
        vs         am       gear       carb 
  0.437500   0.406250   3.687500   2.812500 
> sapply(names(mtcars),~mean(mtcars[[.]]))
Error in match.fun(FUN) : 
  '~mean(mtcars[[.]])' is not a function, character or symbol

Why? Understanding this syntaxis as a function is just a behaviour of some packages as dplyr and purrr? Is it understood by base R for some special cases?

Thank you!


回答1:


As caldwellst says above, the tilda ~ is used to create a formula object, which is a bit of unevaluated code that can be used later. Formulae are not the same as functions, and the purrr functions work with formulae because they make a call to rlang::as_function (via purrr::as_mapper) in the background. The *apply functions obviously don't do this, though you can imitate this behavior by calling one of the above functions in your own modified *apply function (it's better to just use the map functions, the following is just to illustrate the point):

# Write function with `as_mapper()`.
fapply <- function(x, func) {
    sapply(x, purrr::as_mapper(func))
}

# Now we can use formulae.
fapply(names(mtcars), ~ mean(mtcars[[.]]))

#### OUTPUT ####

       mpg        cyl       disp         hp       drat         wt       qsec         vs         am       gear       carb 
 20.090625   6.187500 230.721875 146.687500   3.596563   3.217250  17.848750   0.437500   0.406250   3.687500   2.812500 



回答2:


A formula object is not a function and a formula will only be regarded as a function if the called function that the formula is being passed to has been written to interpret formula arguments as functions. Many of the tidyverse functions are written that way but in general a formula is not by default a function.

fn$

The gusbfn package does have fn$ which will allow just about any function which accepts a function argument to accept formulas. Preface the function call with fn$ and then formula arguments are interpreted as functions (subject to certain rules).

In terms of the example in the question we can do this which does not require writing a special version of sapply to interpret formulas as functions:

library(gsubfn)

fn$sapply(names(mtcars), item ~ mean(mtcars[[item]]))

giving:

       mpg        cyl       disp         hp       drat         wt       qsec 
 20.090625   6.187500 230.721875 146.687500   3.596563   3.217250  17.848750 
        vs         am       gear       carb 
  0.437500   0.406250   3.687500   2.812500 

See ?fn for more information and examples.

match.funfn

The gsubfn package also has match.funfn which is like match.fun in base R except it will also interpret formulas as functions. This lets one write their own functions that accept formula arguments interpreting them as functions.

In terms of the example in the question:

library(gsubfn)

sapplyfn <- function(X, FUN, ...) {
  FUN <- match.funfn(FUN)
  sapply(X, FUN, ...)
}

sapplyfn(names(mtcars), item ~ mean(mtcars[[item]]))

giving:

       mpg        cyl       disp         hp       drat         wt       qsec 
 20.090625   6.187500 230.721875 146.687500   3.596563   3.217250  17.848750 
        vs         am       gear       carb 
  0.437500   0.406250   3.687500   2.812500 

as.function.formula

The gsubfn package also has as.function.formula which will convert a formula to a function. It is used by fn$ and match.funfn. For example,

library(gsubfn)
as.function(item ~ mean(mtcars[[item]]))

giving:

function (item) 
mean(mtcars[[item]])


来源:https://stackoverflow.com/questions/58377184/with-which-generality-can-tilde-operator-be-used-to-build-anonymous-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!