The R Project SVN R

Rev

Rev 71366 | Rev 72027 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 71366 Rev 71460
Line 44... Line 44...
44
}
44
}
45
\value{
45
\value{
46
  The function exists for its side-effect. The definition will be stored in a special metadata object and incorporated in the generic function when the corresponding package is loaded into an R session. 
46
  The function exists for its side-effect. The definition will be stored in a special metadata object and incorporated in the generic function when the corresponding package is loaded into an R session. 
47
  
47
  
48
}
48
}
-
 
49
\section{Method Selection: Avoiding Ambiguity}{
-
 
50
When defining methods, it's important to ensure that methods are
-
 
51
selected correctly; in particular, packages should be designed to
-
 
52
avoid ambiguous method selection.
-
 
53
 
-
 
54
To describe method selection, consider first the case where only one
-
 
55
formal argument is in the active signature; that is, there is only one
-
 
56
argument, \code{x} say, for which methods have been defined.
-
 
57
The generic function has a table of methods, indexed by the class for
-
 
58
the argument in the calls to \code{setMethod}.
-
 
59
If there is a method in the table for the class of \code{x} in the
-
 
60
call, this method is selected.
-
 
61
 
-
 
62
If not, the next best methods would correspond to the direct
-
 
63
superclasses of \code{class(x)}---those appearing in the
-
 
64
\code{contains=} argument when that class was defined.
-
 
65
If there is no method for any of these, the next best would correspond
-
 
66
to the direct superclasses of the first set of superclasses, and so
-
 
67
on.
-
 
68
 
-
 
69
The first possible source of ambiguity arises if the class has several
-
 
70
direct superclasses and methods have been defined for more than one of
-
 
71
those;
-
 
72
\R will consider these equally valid and report an ambiguous choice.
-
 
73
If your package has the class definition for \code{class(x)}, then you
-
 
74
need to define a method explicitly for this combination of generic
-
 
75
function and class.
-
 
76
 
-
 
77
When more than one formal argument appears in the method signature, \R
-
 
78
requires the \dQuote{best} method to be chosen  unambiguously for each
-
 
79
argument.
-
 
80
Ambiguities arise when one method is specific about one argument while
-
 
81
another is specific about a different argument.
-
 
82
A call that satisfies both requirements is then ambiguous:  The two
-
 
83
methods look equally valid, which should be chosen?
-
 
84
In such cases the package needs to add a third method requiring both
-
 
85
arguments to match.
-
 
86
 
-
 
87
The most common examples arise with binary operators.  Methods may be
-
 
88
defined for individual operators, for special groups of operators such as
-
 
89
\code{\link{Arith}} or for group \code{\link{Ops}}.
-
 
90
 
-
 
91
}
49
\details{
92
\section{Details}{
50
The call to \code{setMethod} stores the supplied method definition  in
93
The call to \code{setMethod} stores the supplied method definition  in
51
the metadata table for this generic function in the environment,
94
the metadata table for this generic function in the environment,
52
typically the global environment or the namespace of a package.
95
typically the global environment or the namespace of a package.
53
In the case of a package, the table object becomes part of the namespace or environment of the
96
In the case of a package, the table object becomes part of the namespace or environment of the
54
package.
97
package.
Line 148... Line 191...
148
  Chapman & Hall.
191
  Chapman & Hall.
149
(Chapters 9 and 10.)
192
(Chapters 9 and 10.)
150
}
193
}
151
 
194
 
152
\examples{
195
\examples{
-
 
196
 
-
 
197
## examples for a simple class with two numeric slots.
-
 
198
## (Run example(setMethod) to see the class and function definitions)
153
\dontshow{  require(stats)
199
\dontshow{
154
  setClass("track", slots = c(x="numeric", y = "numeric"))
200
  setClass("track", slots = c(x="numeric", y = "numeric"))
-
 
201
 
155
  setClass("trackCurve", contains = "track", slots = c(smooth = "numeric"))
202
  cumdist <- function(x, y) c(0., cumsum(sqrt(diff(x)^2 + diff(y)^2)))
156
  setClass("trackMultiCurve", slots = c(x="numeric", y="matrix", smooth="matrix"),
203
  setClass("trackMultiCurve", slots = c(x="numeric", y="matrix", smooth="matrix"),
157
          prototype = list(x=numeric(), y=matrix(0,0,0), smooth= matrix(0,0,0)))
204
          prototype = list(x=numeric(), y=matrix(0,0,0), smooth= matrix(0,0,0)))
158
}
-
 
159
 
205
 
160
require(graphics)
206
require(graphics)
-
 
207
}
-
 
208
 
161
## methods for plotting track objects (see the example for \link{setClass})
209
## methods for plotting track objects 
162
##
210
##
163
## First, with only one object as argument:
211
## First, with only one object as argument, plot the two slots
-
 
212
##  y must be included in the signature, it would default to "ANY"
164
setMethod("plot", signature(x="track", y="missing"),
213
setMethod("plot", signature(x="track", y="missing"),
165
  function(x,  y, ...) plot(slot(x, "x"), slot(x, "y"), ...)
214
  function(x,  y, ...) plot(x@x, x@y, ...)
166
)
215
)
-
 
216
 
167
## Second, plot the data from the track on the y-axis against anything
217
## plot numeric data on either axis against a track object
168
## as the x data.
218
## (reducing the track object to the cumulative distance along the track)
-
 
219
## Using a short form for the signature, which matches like formal arguments
169
setMethod("plot", signature(y = "track"),
220
setMethod("plot", c("track", "numeric"),
170
 function(x, y, ...) plot(x, slot(y, "y"), ...)
221
 function(x, y, ...) plot(cumdist(x@x, x@y), y,  xlab = "Distance",...)
171
)
222
)
172
## and similarly with the track on the x-axis (using the short form of
-
 
-
 
223
 
173
## specification for signatures)
224
## and similarly for the other axis
174
setMethod("plot", "track",
225
setMethod("plot", c("numeric", "track"),
175
 function(x, y, ...) plot(slot(x, "y"), y,  ...)
226
 function(x, y, ...) plot(x, cumdist(y@x, y@y),  ylab = "Distance",...)
176
)
227
)
-
 
228
 
177
t1 <- new("track", x=1:20, y=(1:20)^2)
229
t1 <- new("track", x=1:20, y=(1:20)^2)
178
tc1 <- new("trackCurve", t1)
-
 
179
slot(tc1, "smooth") <- smooth.spline(slot(tc1, "x"), slot(tc1, "y"))$y #$
-
 
180
plot(t1)
230
plot(t1)
181
plot(qnorm(ppoints(20)), t1)
231
plot(qnorm(ppoints(20)), t1)
-
 
232
 
182
## An example of inherited methods, and of conforming method arguments
233
## Now a class that inherits from "track", with a vector for data at
-
 
234
## the points 
-
 
235
  setClass("trackData", contains = c("numeric", "track"))
-
 
236
 
-
 
237
 
-
 
238
tc1 <- new("trackData", t1, rnorm(20))
-
 
239
 
-
 
240
 
-
 
241
## a method for plotting the object
183
## (note the dotCurve argument in the method, which will be pulled out
242
## This method has an extra argument, allowed because ... is an
184
## of ... in the generic.
243
## argument to the generic function.
185
setMethod("plot", c("trackCurve", "missing"),
244
setMethod("plot", c("trackData", "missing"),
186
function(x, y, dotCurve = FALSE, ...) {
245
function(x, y, maxRadius = max(par("cin")), ...) {
187
  plot(as(x, "track"))
246
  plot(x@x, x@y, type = "n", ...)
188
  if(length(slot(x, "smooth") > 0))
-
 
189
    lines(slot(x, "x"), slot(x, "smooth"),
247
  symbols(x@x, x@y, circles = abs(x), inches = maxRadius)
190
         lty = if(dotCurve) 2 else 1)
-
 
191
  }
248
  }
192
)
249
)
-
 
250
plot(tc1)
-
 
251
 
193
## the plot of tc1 alone has an added curve; other uses of tc1
252
## Without other methods for "trackData", methods for "track"
194
## are treated as if it were a "track" object.
253
## will be selected by inheritance
195
plot(tc1, dotCurve = TRUE)
-
 
-
 
254
 
196
plot(qnorm(ppoints(20)), tc1)
255
plot(qnorm(ppoints(20)), tc1)
197
 
256
 
198
## defining methods for a special function.
257
## defining methods for primitive function.
199
## Although "[" and "length" are not ordinary functions
258
## Although "[" and "length" are not ordinary functions
200
## methods can be defined for them.
259
## methods can be defined for them.
201
setMethod("[", "track",
260
setMethod("[", "track",
202
  function(x, i, j, ..., drop) {
261
  function(x, i, j, ..., drop) {
203
    x@x <- x@x[i]; x@y <- x@y[i]
262
    x@x <- x@x[i]; x@y <- x@y[i]
Line 206... Line 265...
206
plot(t1[1:15])
265
plot(t1[1:15])
207
 
266
 
208
setMethod("length", "track", function(x)length(x@y))
267
setMethod("length", "track", function(x)length(x@y))
209
length(t1)
268
length(t1)
210
 
269
 
211
## methods can be defined for missing arguments as well
-
 
212
setGeneric("summary") ## make the function into a generic
-
 
213
 
-
 
214
## A method for summary()
270
## Methods for binary operators
215
## The method definition can include the arguments, but
271
## A method for the group generic "Ops" will apply to all operators
216
## if they're omitted, class "missing" is assumed.
272
## unless a method for a more specific operator has been defined.
217
 
-
 
218
setMethod("summary", "missing", function() "<No Object>")
-
 
219
 
273
 
220
\dontshow{% <--------------------> all the following are "assertion" tests
274
## For one trackData argument, go on with just the data part
-
 
275
setMethod("Ops", signature(e1 = "trackData"),
-
 
276
    function(e1, e2) callGeneric(e1@.Data, e2))
221
 
277
 
222
stopifnot(identical(summary(), "<No Object>"))
278
setMethod("Ops", signature(e2 = "trackData"),
-
 
279
    function(e1, e2) callGeneric(e1, e2@.Data))
223
 
280
 
224
removeMethods("summary")
281
## At this point, the choice of a method for a call with BOTH
-
 
282
## arguments from "trackData" is ambiguous.  We must define a method.
225
 
283
 
-
 
284
setMethod("Ops", signature(e1 = "trackData", e2 = "trackData"),
226
## for the primitives
285
    function(e1, e2) callGeneric(e1@.Data, e2@.Data))
-
 
286
## (well, really we should only do this if the "track" part
227
## inherited methods
287
## of the two arguments matched)
228
 
288
 
229
length(tc1)
-
 
230
tc1[-1]
289
tc1 +1
231
 
290
 
232
## make sure old-style methods still work.
-
 
233
t11 <- t1[1:15]
291
1/tc1
234
identical(t1@y[1:15], t11@y)
-
 
235
 
292
 
236
## S3 methods, with nextMethod
-
 
237
form <- y ~ x
293
all(tc1 == tc1)
238
form[1]
-
 
239
 
294
 
240
## S3 arithmetic methods
-
 
241
ISOdate(1990, 12, 1)- ISOdate(1980, 12, 1)
-
 
242
 
-
 
243
## group methods
-
 
244
 
-
 
245
setMethod("Arith", c("track", "numeric"), function(e1, e2){e1@y <-
-
 
246
  callGeneric(e1@y , e2); e1})
-
 
247
 
-
 
248
t1  - 100.
295
\dontshow{
249
t1/2
-
 
250
 
-
 
251
## check it hasn't screwed up S3 methods
-
 
252
ISOdate(1990, 12, 1)- ISOdate(1980, 12, 1)
-
 
253
 
-
 
254
## test the .Generic mechanism
-
 
255
 
-
 
256
setMethod("Compare", signature("track", "track"),
296
removeClass("trackData")
257
  function(e1,e2) {
-
 
258
  switch(.Generic,
-
 
259
   "==" = e1@y == e2@y,
-
 
260
  NA)
-
 
261
 })
-
 
262
 
-
 
263
#stopifnot(all(t1==t1))
297
removeClass("track")
264
#stopifnot(identical(t1<t1, NA))
-
 
265
 
-
 
266
 
-
 
267
## A test of nested calls to "[" with matrix-style arguments
-
 
268
## applied to data.frames (S3 methods)
-
 
269
 
-
 
270
setMethod("[", c("trackMultiCurve", "numeric", "numeric"), function(x, i, j, ..., drop) {
-
 
271
### FIXME:  a better version has only 1st arg in signature
-
 
272
### and uses callNextMethod, when this works with primitives.
-
 
273
    x@y <- x@y[i, j, drop=FALSE]
-
 
274
    x@x <- x@x[i]
-
 
275
    x
-
 
276
})
-
 
277
 
-
 
278
 
-
 
279
"testFunc" <-
-
 
280
function(cur) {
-
 
281
    sorted <- cur[order(cur[,1]),]
-
 
282
    sorted[ !is.na(sorted[,1]), ]
-
 
283
}
298
}
284
 
-
 
285
Nrow <- 1000 # at one time, values this large triggered a bug in gc/protect
-
 
286
## the loop here was a trigger for the bug
-
 
287
Niter <- 10
-
 
288
for(i in 1:Niter)  {
-
 
289
    yy <- matrix(stats::rnorm(10*Nrow), 10, Nrow)
-
 
290
    tDF <- as.data.frame(yy)
-
 
291
    testFunc(tDF)
-
 
292
}
-
 
293
 
-
 
294
 
-
 
295
tMC <- new("trackMultiCurve", x=seq_len(Nrow), y = yy)
-
 
296
## not enough functions have methods for this class to use testFunc
-
 
297
 
-
 
298
stopifnot(identical(tMC[1:10, 1:10]@y, yy[1:10, 1:10]))
-
 
299
 
-
 
300
 
-
 
301
## verify we can remove methods and generic
-
 
302
 
-
 
303
removeMethods("-")
-
 
304
removeMethod("length", "track")
-
 
305
removeMethods("Arith")
-
 
306
removeMethods("Compare")
-
 
307
 
-
 
308
## repeat the test one more time on the primitives
-
 
309
 
-
 
310
length(ISOdate(1990, 12, 1)- ISOdate(1980, 12, 1))
-
 
311
 
-
 
312
removeMethods("length")
-
 
313
 
-
 
314
## methods for \%*\%, which isn't done by the same C code as other ops
-
 
315
 
-
 
316
setClass("foo", slots = c(m="matrix"))
-
 
317
m1 <- matrix(1:12,3,4)
-
 
318
f1 = new("foo", m=m1)
-
 
319
f2 = new("foo", m=t(m1))
-
 
320
 
-
 
321
setMethod("\%*\%", c("foo", "foo"),
-
 
322
          function(x,y) callGeneric(x@m, y@m))
-
 
323
 
-
 
324
stopifnot(identical(f1\%*\%f2, m1\%*\% t(m1)))
-
 
325
 
-
 
326
removeMethods("\%*\%")
-
 
327
removeMethods("plot")
-
 
328
 
-
 
329
if(FALSE) ## Hold until removeMethods revised:
-
 
330
  stopifnot(existsFunction("plot", FALSE) && !isGeneric("plot", 1))
-
 
331
 
-
 
332
## methods for plotData
-
 
333
plotData <- function(x, y, ...) plot(x, y, ...)
-
 
334
 
-
 
335
setGeneric("plotData")
-
 
336
 
-
 
337
setMethod("plotData", signature(x="track", y="missing"),
-
 
338
          function(x,  y, ...) plot(slot(x, "x"), slot(x, "y"), ...))
-
 
339
 
-
 
340
## and now remove the whole generic
-
 
341
removeGeneric("plotData")
-
 
342
 
-
 
343
stopifnot(!exists("plotData", 1))
-
 
344
 
-
 
345
##  Tests of method inheritance & multiple dispatch
-
 
346
setClass("A0", slots = c(a0 = "numeric"))
-
 
347
setClass("A1", contains = "A0", slots = c(a1 = "character"))
-
 
348
 
-
 
349
setClass("B0", slots = c(b0 = "numeric"))
-
 
350
setClass("B1", "B0") # (meaning 'contains = *')
-
 
351
setClass("B2", contains = "B1", slots = c(b2 = "logical"))
-
 
352
 
-
 
353
setClass("AB0", contains = c("A1", "B2"), slots = c(ab0 = "matrix"))
-
 
354
 
-
 
355
f1 <- function(x,  y)"ANY"
-
 
356
 
-
 
357
setGeneric("f1")
-
 
358
 
-
 
359
setMethod("f1", c("A0", "B1"), function(x, y)"A0 B1")
-
 
360
setMethod("f1", c("B1", "A0"), function(x, y)"B1 A0")
-
 
361
 
-
 
362
a0 <- new("A0")
-
 
363
a1 <- new("A1")
-
 
364
b0 <- new("B0")
-
 
365
b1 <- new("B1")
-
 
366
b2 <- new("B2")
-
 
367
 
-
 
368
deparseText <- function(expr)
-
 
369
    paste(deparse(expr), collapse = "\\  ")
-
 
370
 
-
 
371
mustEqual <- function(e1, e2){
-
 
372
    if(!identical(e1, e2))
-
 
373
        stop(paste("!identical(", deparseText(substitute(e1)),
-
 
374
                   ", ", deparseText(substitute(e2)), ")", sep=""))
-
 
375
}
-
 
376
 
-
 
377
mustEqual(f1(a0, b0), "ANY")
-
 
378
mustEqual(f1(a1,b0), "ANY")
-
 
379
mustEqual(f1(a1,b1), "A0 B1")
-
 
380
mustEqual(f1(b1,a1), "B1 A0")
-
 
381
mustEqual(f1(b1,b1), "ANY")
-
 
382
 
-
 
383
## remove classes:  order matters so as not to undefine earlier classes
-
 
384
for(.cl in c("AB0", "A1", "A0", "B2", "B1", "B0"))
-
 
385
    removeClass(.cl)
-
 
386
 
-
 
387
removeGeneric("f1")
-
 
388
 
-
 
389
## test of nonstandard generic definition
-
 
390
 
-
 
391
setGeneric("doubleAnything", function(x) {
-
 
392
  methodValue <- standardGeneric("doubleAnything")
-
 
393
  c(methodValue, methodValue)
-
 
394
})
-
 
395
 
-
 
396
setMethod("doubleAnything", "ANY", function(x)x)
-
 
397
 
-
 
398
setMethod("doubleAnything", "character",
-
 
399
          function(x) paste("<",x,">",sep=""))
-
 
400
 
-
 
401
mustEqual(doubleAnything(1:10), c(1:10, 1:10))
-
 
402
mustEqual(doubleAnything("junk"), rep("<junk>",2))
-
 
403
 
-
 
404
removeGeneric("doubleAnything")
-
 
405
}% dontshow the above
-
 
406
}
299
}
407
 
300
 
408
\seealso{
301
\seealso{
409
 
302
 
410
\link{Methods_for_Nongenerics} discusses method definition for
303
\link{Methods_for_Nongenerics} discusses method definition for