Reputation: 23
In math, when combining functions, you can indicate this using ordinary arithmetic operations, e.g.
u = 2*x
v = log(x)
and then simply
f = u + v
I do a lot of numerical work where you have to construct complicated math operations. It would be very helpful to be able to use a notation more like this one. For example, in R it might look like
f <- function.arithmetic('u+v', args=c('x'))
With some nonstandard evaluation, this might even be as simple as
f(x) %def% u + v
where u and v are already defined functions of x.
Is there a simple way to set up this syntax? For example, breaking down the expression and substituting u(x) and v(x) wherever they occur -- then doing an ordinary eval. (I would trust an existing parsing function more than some hack that I write. And I know that "parse" doesn't do this.)
Answers already suggested would work, but they seem more complicated than writing
f <- function(x) u(x) + v(x)
If the expression is more complicated, this notation starts getting harder to read. I want to set up something simpler, more easily readable, and closer to the above mathematical notation.
Upvotes: 2
Views: 178
Reputation: 269945
Here are a couple of approaches:
1) Ops/Math This could be done using S3 or S4. We illustrate S3 only.
Compose <- function(f, g) {
if (missing(g)) structure(f, class = c("functional", "function"))
else Compose(function(...) f(g(...)))
}
Ops.functional <- function(e1, e2) {
op <- match.fun(.Generic)
Compose( if (is.numeric(e1)) function(...) op(e1, e2(...))
else if (is.numeric(e2)) function(...) op(e1(...), e2)
else function(...) op(e1(...), e2(...)))
}
Math.functional <- function(x, ...) {
op <- match.fun(.Generic)
Compose(op, x)
}
Here are two examples:
u <- Compose(function(x) 2*x)
v <- Compose(log)
(u + v)(pi/2) # example 1
## [1] 3.593175
(exp(u*v) / (1 + u^2 + v^2)) (pi/2) # example 2
## [1] 0.3731149
Note: that u
could have alternately been defined as u <- 2 * Compose(identity)
. In fact, we could define:
Id <- Compose(identity)
u <- 2*Id
v <- log(Id)
2) Define your own functions This is not really much work. Likely less than a page to define all common functions. This could be done using the %...% infix operators but if you really want to go the infix route (1) above seems preferable. So with this approach we keep it simple. The following could be enhanced to allow numeric arguments to be regarded as constant functions as we did in (1).
Plus <- function(f, g) function(...) f(...) + g(...)
Plus(function(x) 2*x, log)(pi/2)
## [1] 3.593175
Upvotes: 4
Reputation: 263451
This is G.Grothendieck's answer cast in the form of an infix "+"-operator:
`%+%` <- function(f1, f2) { function(x) {f1(x) +f2(x)} }
f <- cos %+% sin
f
#-----
function(x) {f1(x) +f2(x)}
<environment: 0x7ffccd7eeea8>
#-------
f(0)
#[1] 1
There are also examples of functional composition on the 'funprog' page which needs to be pulled up with one of the function names, for instance?Reduce
. Can also be defined to accept additional arguments:
`%+%` <- function(f1, f2) { function(x,...) {f1(x,...) +f2(x,...)} }
f2 <- dt %+% dt
#-- testing---
> f2(0)
Error in f1(x, ...) : argument "df" is missing, with no default
> f2(0, 6)
[1] 0.7654655
> dt(0,6)
[1] 0.3827328
To see how this is handled internally you can recover the definition by examining the environment stored with the resulting closure:
> ls(envir=environment(f2) )
[1] "f1" "f2"
> environment(f2)$f1
function (x, df, ncp, log = FALSE)
{
if (missing(ncp))
.Call(C_dt, x, df, log)
else .Call(C_dnt, x, df, ncp, log)
}
<bytecode: 0x7ffcc63e8ff8>
<environment: namespace:stats>
The problem with your example is that you did not define the u and v in an R-functional manner. Presumably this would not be the situation with your proposed use case.
> u = function(x,...)2*x
> v = function(x,...) (log(x))
> f <- u %+% v
> f(4)
[1] 9.386294
Some of this style of programming may be supported by Hadley's lazyeval
package:
> require(lazyeval)
Loading required package: lazyeval
> help(pac=lazyeval)
> lazy_eval(interp(~ u + v, u = sum(1:10), v=sum(1:5)) )
[1] 70
> x <- 1:10; y=1:5
> lazy_eval(interp(~ u + v, u = sum(x), v=sum(y)) )
[1] 70
> lazy_eval(interp(~ u / v, u = sum(x), v=sum(y)) )
[1] 3.666667
> lazy_eval(interp(~ log(u) / v, u = sum(x), v=sum(y)) )
[1] 0.2671555
But I have encountered some puzzles that I cannot really understand:
e2 <- ~ exp(u * v)/(1 + x)^2
lazy_eval(interp(e2, u = sum(x)/100, v=sum(y)/100) )
#[1] 0.271499668 0.120666519 0.067874917 0.043439947 0.030166630 0.022163238 0.016968729
#[8] 0.013407391 0.010859987 0.008975196
exp( sum(x)/100 +sum(y)/100 )/(1+x)^2
[1] 0.50343818 0.22375030 0.12585954 0.08055011 0.05593758 0.04109699 0.03146489 0.02486114
[9] 0.02013753 0.01664258
Upvotes: 5