I have a list of parameters and would like to call B(var3=list(1:3)) via do.call. But the following example only calls the method for dispatch "missing". How do I access the method for dispatch "ANY"?
B.initialize<-function(..., var3=list()){
callSuper(..., var3=as.list(var3))
}
.B<-setRefClass(Class = "B"
,fields = list(var3 = "list")
,methods = list(initialize=B.initialize))
setGeneric("B", function(x, ...) standardGeneric("B"))
setMethod("B", "missing", function(x, ...) {
.B()
})
setMethod("B", "ANY", function(x, ...) {
.Part(var1=x, ...)
})
do.call(B,list(var3=list(1:3)))
Related
I have some trouble understanding the differences in method dispatching between S3 and S4 classes. As far as I understand, S3 classes use UseMethod and finds the correct method via the class attribute of the object passed. S4 classes use StandardGeneric and work with function signatures (I'm reading Advanced R). But the following code runs:
myfun <- function(x, y = NULL) {
UseMethod("myfun")
}
myfun.default <- function(x, y) {
print("default method")
}
myfun.integer <- function(x, y) {
print("S3 dispatch for integer")
}
setMethod("myfun",
signature = c(x = "matrix", y = "ANY"),
function(x, y) {
print("S4 dispatch for matrices")
}
)
setMethod("myfun",
signature = c(x = "character", y = "ANY"),
function(x, y) {
print("S4 dispatch for strings")
}
)
setMethod("myfun",
signature = c(x = "character", y = "character"),
function(x, y) {
print("S4 dispatch for string + string")
}
)
myfun(iris)
## [1] "default method"
myfun(1:10)
## [1] "S3 dispatch for integer"
myfun(matrix(0, nrow = 2, ncol = 2))
## [1] "S4 dispatch for matrices"
myfun("foo")
## [1] "S4 dispatch for strings"
myfun("foo", y = "bar")
## [1] "S4 dispatch for string + string"
What exactly is going on here? I created an S3 method called "myfun", for which the S3 method dispatch works as intended. So far, so good.
But this S3 method also correctly dispatches S4 methods, even though I didn't define a StandardGeneric for these S4 methods (or convert myfun to such). How come? Any background would be appreciated.
Thanks in advance!
I would like to have a variable number of arguments in my S4 generic myMethod such that these are both valid:
myMethod(100)
myMethod(100, 200)
Here is my attempt at a definition:
setGeneric(
"myMethod",
function(x) {
standardGeneric("myMethod")
})
setMethod(
"myMethod",
signature = c("numeric"),
definition = function(x) {
print("MyMethod on numeric")
})
setMethod(
"myMethod",
signature = c("numeric", "numeric"),
definition = function(x, y) {
print("MyMethod on numeric, numeric")
})
However, this gives the error:
Error in matchSignature(signature, fdef) :
more elements in the method signature (2) than in the generic signature (1) for function ‘myMethod’
It's worth clarifying whether you want to dispatch (select a method) on more than one argument (in which case include all argument names in the signature= of setGeneric())
setGeneric("fun", function(x, y) standardGeneric("fun"),
signature=c("x", "y"))
setMethod("fun", c(x="numeric", y="numeric"), function(x, y) {
"fun,numeric,numeric-method"
})
versus dispatching based on the first (include only the first argument in signature=) and either require all methods to have additional arguments (name the arguments in the generic function)
setGeneric("fun", function(x, y) standardGeneric("fun"),
signature="x")
setMethod("fun", c(x="numeric"), function(x, y) {
"fun,numeric-method"
})
or only some methods (use ... in the generic, and name the arguments in the method).
setGeneric("fun", function(x, ...) standardGeneric("fun"),
signature="x")
setMethod("fun", c(x="numeric"), function(x, y) {
"fun,numeric-method"
})
Your generic should support 2 arguments.
setGeneric(
"myMethod",
function(x, y) {
standardGeneric("myMethod")
})
Also the function in your second method should actually support two arguments:
setMethod(
"myMethod",
signature = c("numeric", "numeric"),
definition = function(x, y) {
print("MyMethod on numeric, numeric")
})
More generally, if you want to specify arbitrarily many arguments, you should have a look at the elipsis argument ....
I would like to write a [. method for my ReferenceClass. So far, I have something like this:
DT <- data.table(Index=1:5)
MySeries <- setRefClass("MySeries", fields = list(data="data.table"))
setMethod("[","MySeries",function(x, i,j,drop) {
ii <- substitute(i)
x$data <- x$data[eval(ii)]
return(x)
})
S <- MySeries(data=DT)
... but it throws an error when I finally call S[Index>3]. How to fix the above to get this expected result?
Index
4: 4
5: 5
This is really about the use of eval(substitute()) as much as about S4 methods. Here is the generic that you are interested in
> getGeneric("[")
standardGeneric for "[" defined from package "base"
function (x, i, j, ..., drop = TRUE)
standardGeneric("[", .Primitive("["))
<bytecode: 0x42f4fe0>
<environment: 0x3214270>
Methods may be defined for arguments: x, i, j, drop
Use showMethods("[") for currently available ones.
Your method signature differs from the generic (no '...' and no default for 'drop') so the method has a nested '.local' function
> getMethod("[", "MySeries")
Method Definition:
function (x, i, j, ..., drop = TRUE)
{
.local <- function (x, i, j, drop)
{
ii <- substitute(i)
x$data <- x$data[eval(ii)]
return(x)
}
.local(x, i, j, ..., drop)
}
Signatures:
x
target "MySeries"
defined "MySeries"
and subsitute(i) is not what you think it is. Instead, write a method matching the generic signature
setMethod("[", "MySeries", function(x, i, j, ..., drop=TRUE) {
x$data <- x$data[eval(substitute(i))]
x
})
nested functions are a general problem with the eval(substitute()) paradigm, not just definition of S4 methods; see this question.
How can I avoid the classic Error: argument "<argname>" is missing, with no default error (see example below) when explicitly dispatching argument values to subsequent S4 methods in a given S4 method.
Example
Big picture
Whe have a method foo() that calls method bar().
Both methods depend on arguments x and y.
Method foo() dispatches arguments x and y to bar() in an explicit way: bar(x=x, y=y).
Now, the crucial point here is that I don't want foo() to care if any or all of the arguments that are passed along to bar() are missing.
Generic methods
setGeneric(
name="foo",
signature=c("x", "y"),
def=function(x, y, ...) {
standardGeneric("foo")
}
)
setGeneric(
name="bar",
signature=c("x", "y"),
def=function(x, y, ...) {
standardGeneric("bar")
}
)
Methods for bar()
setMethod(
f="bar",
signature=signature(x="missing", y="missing"),
definition=function(x, y, ...) {
print("Doing what I'm supposed to do when both args are missing")
return(NULL)
}
)
setMethod(
f="bar",
signature=signature(x="ANY", y="missing"),
definition=function(x, y, ...) {
message("'y' is missing, but I can give you 'x':")
print(x)
return(NULL)
}
)
setMethod(
f="bar",
signature=signature(x="missing", y="ANY"),
definition=function(x, y, ...) {
message("'x' is missing, but I can give you 'y':")
print(y)
return(NULL)
}
)
setMethod(
f="bar",
signature=signature(x="ANY", y="ANY"),
definition=function(x, y, ...) {
message("x:")
print(x)
message("y:")
print(y)
return(NULL)
}
)
Method for foo()
As mentioned above, I don't want foo() to care if any or all of the arguments that are passed along to bar() are missing. It is just supposed to pass everything along to bar() in an explicit manner:
setMethod(
f="foo",
signature=signature(x="ANY", y="ANY"),
definition=function(x, y, ...) {
bar(x=x, y=y)
}
)
The method def might look good on first sight, but it will fail if either x or y are missing when calling it:
> foo(x="Hello", y="World!")
x:
[1] "Hello"
y:
[1] "World!"
NULL
> foo(x="Hello")
Error in bar(x = x, y = y) :
error in evaluating the argument 'y' in selecting a method for function 'bar': Error: argument "y" is missing, with no default
> foo()
Error in bar(x = x, y = y) :
error in evaluating the argument 'x' in selecting a method for function 'bar': Error: argument "x" is missing, with no default
Workaround
This is the only workaround that I could come up with so far:
setMethod(
f="foo",
signature=signature(x="ANY", y="ANY"),
definition=function(x, y, ...) {
if (missing(x) && missing(y)) {
bar()
} else if (missing(x)) {
bar(y=y)
} else if (missing(y)) {
bar(x=x)
} else {
bar(x=x, y=y)
}
}
)
> foo(x="Hello", y="World!")
x:
[1] "Hello"
y:
[1] "World!"
NULL
> foo(x="Hello")
'y' is missing, but I can give you 'x':
[1] "Hello"
NULL
> foo(y="World!")
'x' is missing, but I can give you 'y':
[1] "World!"
NULL
> foo()
[1] "Doing what I'm supposed to do when both args are missing"
NULL
It works, but I don't really like it because of all the if ... else statements. The whole "if-else logic" already went in the specification of the various methods for bar(). After all, that's the whole point of having a method dispatcher in the first place, right? Hence I would consider the statements as "undesired work" and I'm looking for a better way.
One could of course resort to to using NULL as default value for all "critical" arguments, but I would like to rely on missing() instead of is.null() in my functions as much as possible.
Here is an alternative idea. (It's broadly inspired by the sort of "computing on the language" used by many of R's model-fitting functions.)
setMethod(
f="foo",
signature=signature(x="ANY", y="ANY"),
definition=function(x, y, ...) {
mc <- match.call()
mc[[1]] <- quote(bar)
eval(mc)
}
)
foo(x="Hello")
# 'y' is missing, but I can give you 'x':
# [1] "Hello"
# NULL
foo(y="World")
# 'x' is missing, but I can give you 'y':
# [1] "World"
# NULL
foo()
# [1] "Doing what I'm supposed to do when both args are missing"
# NULL
In R with S4 classes, I define a method to assign two values to an object:
setGeneric("setValues", function(object, x, y) {
standardGeneric("setValues")
})
setMethod(f = "setValues", signature = "chart", definition = function(object, x, y) {
object#x <- x
object#y <- y
return(object)
})
and then use it as follows
obj <- setValues(obj, "value_X", "value_Y")
But, this means that I have to write obj as an argument to the function all the time.
Is it possible to define a method in S4 that could be used without the object itself as an argument?
For example:
obj <- setValues("value_X", "value_Y")
I am very grateful for any kind of suggestions! :)