Menu
Coddy logo textTech

Dispatch sur deux arguments

Fait partie de la section Programmation orientée objet du Journey R de Coddy. Leçon 29 sur 57.

Un générique S4 peut choisir sa méthode en fonction des classes de plusieurs arguments. La signature répertorie une classe par argument, de sorte que chaque paire de classes puisse avoir sa propre méthode :

setClass("Asteroid", slots = c(size = "numeric"))
setClass("Ship", slots = c(name = "character"))
invisible(setGeneric("collide", function(a, b) standardGeneric("collide")))
setMethod("collide", signature("Asteroid", "Ship"), function(a, b) paste(b@name, "is hit"))
setMethod("collide", signature("Ship", "Ship"), function(a, b) "two ships bump")
rock <- new("Asteroid", size = 3)
enterprise <- new("Ship", name = "Enterprise")
print(collide(rock, enterprise))
print(collide(enterprise, enterprise))

Résultat :

[1] "Enterprise is hit"
[1] "two ships bump"

La classe "ANY" correspond à chaque classe. Une méthode pour signature("ANY", "ANY") est la solution de repli qui s’exécute lorsqu’aucune paire plus spécifique ne correspond :

setClass("Ship", slots = c(name = "character"))
invisible(setGeneric("collide", function(a, b) standardGeneric("collide")))
setMethod("collide", signature("Ship", "Ship"), function(a, b) "two ships bump")
setMethod("collide", signature("ANY", "ANY"), function(a, b) "nothing happens")
s <- new("Ship", name = "A")
cat(collide(s, s), "/", collide(s, 42), "/", collide("x", "y"))

Sortie :

two ships bump / nothing happens / nothing happens

L’ordre des arguments est important : signature("A", "B") et signature("B", "A") sont des méthodes différentes. Des classes de base telles que numeric et character peuvent également apparaître dans une signature :

invisible(setGeneric("join", function(x, y) standardGeneric("join")))
setMethod("join", signature("character", "numeric"), function(x, y) paste(x, "number", y))
setMethod("join", signature("numeric", "character"), function(x, y) paste(y, "after number", x))
print(join("item", 3))
print(join(3, "item"))

Sortie :

[1] "item number 3"
[1] "item after number 3"

La classe "missing" dans une signature correspond à un argument qui n’a pas du tout été fourni, de sorte qu’un générique peut proposer une forme courte et une forme longue :

invisible(setGeneric("greet", function(name, greeting) standardGeneric("greet")))
setMethod("greet", signature("character", "missing"), function(name, greeting) paste("Hello,", name))
setMethod("greet", signature("character", "character"), function(name, greeting) paste0(greeting, ", ", name))
print(greet("Ada"))
print(greet("Ada", "Good morning"))

Sortie :

[1] "Hello, Ada"
[1] "Good morning, Ada"
challenge icon

Défi

Facile

Les tarifs d’expédition dépendent de l’article et de la zone. Les classes sont déclarées dans Item.R (Letter, Box, chacune avec un slot kg) et Zone.R (Domestic, Abroad, chacune avec un slot name). Dans Rates.R, créez le générique shipping(item, zone) et une méthode pour chaque paire :

  • Letter à domicile : 1 ; à l’étranger : 3.
  • Box à domicile : 5 plus 2 par kg ; à l’étranger : 12 plus 4 par kg.
  • Toute autre paire : arrêtez l’exécution avec no rate for this item and zone (utilisez call. = FALSE).

Le code fourni lit des lignes box home 2.5 (article, zone, kg), où les mots inconnus restent du texte simple, et affiche box to home: 10.00 ou error: suivi du message.

Votre code va dans Item.R, Zone.R et Rates.R. main.R contient le code fourni d’entrée/sortie et ne peut pas être modifié.

Essayez vous-même

source("Item.R")
source("Zone.R")
source("Rates.R")

# Code d'entrée/sortie fourni : le garder tel quel
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  kg <- as.numeric(p[3])
  item <- switch(p[1], letter = new("Letter", kg = kg), box = new("Box", kg = kg), kg)
  zone <- switch(p[2], home = new("Domestic", name = "home"), abroad = new("Abroad", name = "abroad"), p[2])
  out <- tryCatch(sprintf("%s to %s: %.2f", p[1], p[2], shipping(item, zone)),
                  error = function(e) paste("error:", conditionMessage(e)))
  cat(out, "\n", sep = "")
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Compilateur R en ligne