I have a generic function called foo
. It operates differently on the classes bar
and baz
but there is some shared pre-processing that needs to only be done once. What is the idiomatic R way of carrying this out?
In my silly example below, I want to multiply the object being passed to the generic by 10. Yet when the method is called, the original value is printed to the console. I have read the language definition and know that the below pattern does not work. My question is: Where or how should I handle shared processing of generic arguments before calling the method?
a <- structure(1:5, class="bar")
b <- structure(6:10, class="baz")
foo <- function(x) {
x <- x * 10 # where should shared preprocessing go?
UseMethod("foo")
}
foo.bar <- function(x) {
cat("Foo!", x)
}
foo.baz <- function(x) {
cat("Baz!", x)
}
# does not propagate the operations carried out `foo`
foo(a)
foo(b)
1) Layer foo on top of actual generic Change foo
to perform the preliminary code and invoke foo_
a new generic as shown. Rename foo.bar
and foo.baz
to foo_.bar
and foo_.baz
respectively so that we are left with (have also added newlines to the example):
foo <- function(x) {
x <- x * 10
foo_(x)
}
foo_ <- function(x) UseMethod("foo_")
foo_.bar <- function(x) cat("Foo!", x, "\n")
foo_.baz <- function(x) cat("Baz!", x, "\n")
Now test it out:
a <- structure(1:5, class="bar")
b <- structure(6:10, class="baz")
foo(a)
## Foo! 10 20 30 40 50
foo(b)
## Baz! 60 70 80 90 100
For an example of this in a widely used package see the source of dplyr::mutate
2) NextMethod Another way would be to give every object a class vector of two classes with "foo"
made a subclass of "bar"
in the case of a
and of "baz"
in the case of b
. Then use NextMethod
. Solution (1) seems simpler and it may seem weird that "foo"
is a subclass of both "bar"
and "baz"
but here is an example of this one just in case:
foo <- function(x) UseMethod("foo")
foo.foo <- function(x) {
x <- x * 10
NextMethod()
}
foo.bar <- function(x) cat("Foo!", x, "\n")
foo.baz <- function(x) cat("Baz!", x, "\n")
Test it noting that we have changed the definitions of a
and b
so that they work with this approach:
a <- structure(1:5, class= c("foo", "bar"))
b <- structure(6:10, class = c("foo", "baz"))
foo(a)
## Foo! 10 20 30 40 50
foo(b)
## Baz! 60 70 80 90 100
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With