问题
Is there a way to write pipelined functions in R where the result of one function passes immediately into the next? I'm coming from F# and really appreciated this ability but have not found how to do it in R. It should be simple but I can't find how. In F# it would look something like this:
let complexFunction x =
x |> square
|> add 5
|> toString
In this case the input would be squared, then have 5 added to it and then converted to a string. I'm wanting to be able to do something similar in R but don't know how. I've searched for how to do something like this but have not come across anything. I'm wanting this for importing data because I typically have to import it and then filter. Right now I do this in multiple steps and would really like to be able to do something the way you would in F# with pipelines.
回答1:
We can use Compose
from the functional package to create our own binary operator that does something similar to what you want
# Define our helper functions
square <- function(x){x^2}
add5 <- function(x){x + 5}
# functional contains Compose
library(functional)
# Define our binary operator
"%|>%" <- Compose
# Create our complexFunction by 'piping' our functions
complexFunction <- square %|>% add5 %|>% as.character
complexFunction(1:5)
#[1] "6" "9" "14" "21" "30"
# previously had this until flodel pointed out
# that the above was sufficient
#"%|>%" <- function(fun1, fun2){ Compose(fun1, fun2) }
I guess we could technically do this without requiring the functional package - but it feels so right using Compose
for this task.
"%|>%" <- function(fun1, fun2){
function(x){fun2(fun1(x))}
}
complexFunction <- square %|>% add5 %|>% as.character
complexFunction(1:5)
#[1] "6" "9" "14" "21" "30"
回答2:
Here is a functional programming approach using Reduce
. It is in fact an example from ?Reduce
square <- function(x) x^2
add_5 <- function(x) x+5
x <- 1:5
## Iterative function application:
Funcall <- function(f, ...) f(...)
Reduce(Funcall, list(as.character, add_5, square,x), right = TRUE)
## [1] "6" "9" "14" "21" "30"
Or even more simply using the functional
package and Compose
This is nice as it will create the function for you
library(functional)
do_stuff <- Compose(square,add_5,as.character )
do_stuff(1:5)
## [1] "6" "9" "14" "21" "30"
I note that I would not consider either of these approaches idiomatically R
ish (if that is even a phrase)
回答3:
I think that you might just want to write a function to do the steps you desire.
complexFunction <- function(x) {
as.character(x^2 + 5)
}
Then just call complexFunction(x)
.
Edit to show what R is doing internally (@mnel) -- The way R
parses the and evaluates as.character(x^2 + 5)
does what you want
You can use codetools
to investigate what R
to see how the values are being passed to eachother
flattenAssignment(quote(as.character(x^2+5)))
[[1]]
[[1]][[1]]
x
[[1]][[2]]
`*tmp*`^2
[[1]][[3]]
`*tmp*` + 5
[[2]]
[[2]][[1]]
`as.character<-`(`*tmp*`, value = `*tmpv*`)
[[2]][[2]]
`+<-`(`*tmp*`, 5, value = `*tmpv*`)
[[2]][[3]]
`^<-`(x, 2, value = `*tmpv*`)
Or you can get the Lisp style representation to see how it is parsed (and the results passed)
showTree(quote(as.character(x^2+5)))
(as.character (+ (^ x 2) 5))
回答4:
Since this question was asked, the magrittr pipe has become enormously popular in R. So your example would be:
library (magrittr)
fx <- function (x) {
x %>%
`^` (2) %>%
`+` (5) %>%
as.character ()
}
Note that the backquote notation is because I'm literally using R's built-in functions and I need to specially quote them to use them in this manner. More normally-named functions (like exp
or if I'd created a helper function add
) wouldn't need backquotes and would appear more like your example.
Note also that %>%
passes the incoming value as the first argument to the next function automatically, though you can change this. Note also that an R function returns the last value calculated so I don't need to return
or assign the calculation in order to return it.
This is a lot like the nice special operators defined by other answers, but it uses a particular function that's widely used in R now.
回答5:
This works for R pretty similar in F#:
"%|>%" <- function(x, fun){
if(is.function(x)) {
function(...) fun(x(...))
} else {
fun(x)
}
}
来源:https://stackoverflow.com/questions/13354048/r-pipelining-functions