The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
vfunc
package: adding two functions in RIn mathematics, given two functions , it is natural
to define as the function that maps to . However, in base R, objects of class
function
do not have arithmetic methods defined, so idiom
such as f + g
returns an error, even though it has a
perfectly reasonable expectation. The vfunc package
offers
this functionality. Other similar features are provided, which lead to
compact and readable idiom. A wide class of coding bugs is
eliminated.
Consider the following R session:
<- function(x){x^2}
f <- function(x){1/(1-x)}
g + g
f #> Error in f + g: non-numeric argument to binary operator
Above, there is a reasonably clear expectation for
f + g
: it should give a function that returns the sum of
f()
and g()
; something like
function(x){f(x) + g(x)}
. However, it returns an error
because f
and g
are objects of S4
class function
, which do not have an addition method. The
vfunc
package allows us to do this.
The package is designed so that objects of class vf
operate as functions but are subject to arithmetic operations, which are
executed transparently. For example:
library("vfunc")
#>
#> Attaching package: 'vfunc'
#> The following object is masked from 'package:stats':
#>
#> Gamma
<- as.vf(f)
f <- as.vf(g)
g + g)(1:10)
(f #> [1] Inf 3.00000 8.50000 15.66667 24.75000 35.80000 48.83333 63.85714
#> [9] 80.87500 99.88889
Further, functions may be combined arithmetically:
+ 4*g - f*g)(1:10)
(f #> [1] NaN 4.00000 11.50000 20.00000 30.25000 42.40000 56.50000
#> [8] 72.57143 90.62500 110.66667
or compositionally:
f(g) + g(f))(1:10)
(#> [1] Inf 0.666666667 0.125000000 0.044444444 0.020833333 0.011428571
#> [7] 0.006944444 0.004535147 0.003125000 0.002244669
The advantages of such idiom fall in to two main categories. Firstly,
code can become considerably more compact; and secondly one can guard
against a wide class of hard-to-find bugs. Now consider f()
and g()
to be trivariate functions, each taking three
arguments, say,
<- function(x,y,z){x + x*y - x/z}
f <- function(x,y,z){x^2 - z} g
and , , . Given this, we wish to calculate
How would one code up such an expression in R? The standard way would be
<- 1.2
x <- 1.7
y <- 4.3
z f(x,y,z) + g(x,y,z))*(f(x,y,z) + 4 - 2*f(x,y,z)*g(x,y,z))
(#> [1] 2.411975
Note the repeated specification of argument list
(x,y,z)
, repeated here five times. Now use the
vfunc
package:
<- as.vf(f)
f <- as.vf(g)
g + g)*(f + 4 - 2*f*g))(x,y,z)
((f #> [1] 2.411975
See how the package allows one to ‘’factorize’’ the argument list so it appears once, leading to more compact code. It is also arguably less error-prone, as the following example illustrates. Consider
(such expressions arise in the study of dynamical systems). Note that functions and are to be evaluated with two distinct sets of arguments at different levels of nesting, namely at the inner level and at the outer. Standard R idiom would be
f(x + z, y + z, f(x, x, y) - g(x, x, y)) + g(x + z, y + z, f(x, x, y) - g(x, x, y))
#> [1] 64.04918
The author can attest that finding bugs in such expressions can be
difficult [it is easy to mistype (x,x,y)
in one of its
occurrences, yet difficult to detect the error]. However,
vfunc
idiom would be
+ g)(x + z, y + z, (f - g)(x, x, y))
(f #> [1] 64.04918
which is certainly shorter, arguably neater and at least the author
finds such constructions considerably less error-prone. In this form,
one can be sure that both f()
and g()
are
called with identical arguments at each of the two levels in the
expression, as the arguments appear only once.
The package includes functions such as Sin()
which is a
vf
equivalent to base::sin()
. This allows one
to define composite functions such as
<- as.vf(function(x,y){Cos(x) + Sin(x-y)})
j <- as.vf(function(x,y){Tan(x) + Log(x+y)})
k <- as.vf(function(x,y){Sin(x/2) + x^2 }) l
(note that functions j()
, k()
and
l()
are bivariate). Then compare
+ k + l)(Sin + Log, Cos + Exp)(Sin + Tan)(0.4)
(j #> [1] 2.545235
with the one-stage idiom which reads:
j(sin(sin(0.4) + tan(0.4)) + log(sin(0.4) + tan(0.4)), cos(sin(0.4) + tan(0.4)) +
exp(sin(0.4) + tan(0.4))) + k(sin(sin(0.4) + tan(0.4)) + log(sin(0.4) + tan(0.4)),
cos(sin(0.4) + tan(0.4)) + exp(sin(0.4) + tan(0.4)))+ l(sin(sin(0.4) + tan(0.4)) +
log(sin(0.4) + tan(0.4)), cos(sin(0.4) + tan(0.4)) + exp(sin(0.4) + tan(0.4)))
#> [1] 2.545235
and the multi-stage idiom:
<- function(x,y){j(x,y) + k(x,y) + l(x,y)}
A <- function(x){sin(x) + log(x)}
B <- function(x){cos(x) + exp(x)}
C <- function(x){sin(x) + tan(x)}
D <- 0.4
x A(B(D(x)), C(D(x)))
#> [1] 2.545235
See how the one-stage idiom is very long, and the multi-stage idiom
is opaque [and nevertheless has repeated instances of (x,y)
and x
].
The vfunc
package allows functions to be ‘’factorized’’,
that is, f(x) + g(x)
to be re-written
(f + g)(x)
. This allows for concise idiom and eliminates a
certain class of coding errors. The package also allows for recursive
application of such ideas.
For more detail, see the package vignette
vignette("vfunc")
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.