web-dev-qa-db-fra.com

Programmation orientée objet et R

La programmation orientée objet d'une manière ou d'une autre est tout à fait possible dans R. Cependant, contrairement à Python par exemple, il existe de nombreuses façons de réaliser l'orientation objet:

Ma question est:

Quelles différences principales distinguent ces façons de OO programmation dans R?

Idéalement, les réponses ici serviront de référence aux programmeurs R essayant de décider quelles méthodes de programmation OO répondent le mieux à leurs besoins).

À ce titre, je demande des détails, présentés de manière objective, fondés sur l'expérience et étayés par des faits et des références. Points bonus pour clarifier comment ces méthodes correspondent aux pratiques standard OO.

78
Paul Hiemstra

Classes S3

  • Pas vraiment des objets, plus une convention de nommage
  • Basé autour du. syntaxe: par ex. pour l'impression, print appelle print.lmprint.anova, etc. Et s'il n'est pas trouvé, print.default

Classes S4

Classes de référence

proto

  • ggplot2 a été initialement écrit en proto, mais sera finalement réécrit en utilisant S3.
  • Concept soigné (prototypes, pas classes), mais semble délicat en pratique
  • La prochaine version de ggplot2 semble s'en éloigner
  • Description du concept et de la mise en œuvre

Classes R6

  • Par référence
  • Ne dépend pas des classes S4
  • " Création une classe R6 est similaire à la classe de référence, sauf qu'il n'est pas nécessaire de séparer les champs et les méthodes, et vous ne pouvez pas spécifier les types de champs."
33
Ari B. Friedman

Modifier le 3/8/12: La réponse ci-dessous répond à une partie de la question initialement publiée qui a depuis été supprimée. Je l'ai copié ci-dessous, pour fournir le contexte de ma réponse:

Comment les différentes méthodes OO correspondent-elles aux méthodes OO plus standard utilisées par ex. Java ou Python?


Ma contribution se rapporte à votre deuxième question, sur la façon dont les méthodes OO de R correspondent à des méthodes OO plus standard. Comme j'y ai pensé par le passé, je suis revenu encore et encore sur deux passages, l'un de Friedrich Leisch et l'autre de John Chambers. Les deux font un bon travail pour expliquer pourquoi la programmation de type OO dans R a une saveur différente de celle dans de nombreuses autres langues.

Tout d'abord, Friedrich Leisch, de "Création de packages R: un tutoriel" ( avertissement: PDF ):

S est rare car il est à la fois interactif et possède un système d'orientation des objets. Concevoir des classes est clairement de la programmation, mais pour rendre S utile comme environnement interactif d'analyse de données, il est logique que ce soit un langage fonctionnel. Dans les "réels" langages de programmation orientée objet (POO) comme C++ ou Java les définitions de classe et de méthode sont étroitement liées, les méthodes font partie des classes (et donc des objets). Nous voulons des ajouts incrémentiels et interactifs comme des méthodes définies par l'utilisateur pour des classes prédéfinies. Ces ajouts peuvent être effectués à tout moment, même à la volée à l'invite de ligne de commande pendant que nous analysons un ensemble de données. S essaie de faire un compromis entre l'orientation de l'objet et l'utilisation interactive, et bien que les compromis ne soient jamais optimaux par rapport à tous les objectifs qu'ils essaient d'atteindre, ils fonctionnent souvent étonnamment bien dans la pratique.

L'autre passage provient du superbe livre de John Chambers "Software for Data Analysis" . ( Lien vers le passage cité ):

Le modèle de programmation OOP diffère du langage S sur tous les points sauf le premier, même si S et certains autres langages fonctionnels prennent en charge les classes et les méthodes. Les définitions de méthode dans un système OOP sont locales à la classe; il n'est pas nécessaire que le même nom pour une méthode signifie la même chose pour une classe non liée. En revanche, les définitions de méthode dans R ne résident pas dans une définition de classe; conceptuellement, ils sont associés à la fonction générique. Les définitions de classe entrent dans la détermination de la sélection de méthode, directement ou par héritage. Les programmeurs habitués au modèle OOP sont parfois frustrés ou confus que leur programmation ne soit pas transférée directement vers R, mais ce n'est pas le cas. L'utilisation fonctionnelle des méthodes est plus compliquée mais aussi plus adaptée aux fonctions significatives et ne peut pas être réduite à la version OOP.

19
Josh O'Brien

S3 et S4 semblent être les approches officielles (c'est-à-dire intégrées) pour la programmation OO. J'ai commencé à utiliser une combinaison de S3 avec des fonctions intégrées dans la fonction/méthode constructeur. Mon objectif était d'avoir un object $ method () type syntaxe de sorte que j'ai des champs semi-privés. Je dis semi-privés parce qu'il n'y a aucun moyen de les cacher vraiment (pour autant que je sache). Voici un exemple simple qui ne fait rien :

#' Constructor
EmailClass <- function(name, email) {
    nc = list(
        name = name,
        email = email,
        get = function(x) nc[[x]],
        set = function(x, value) nc[[x]] <<- value,
        props = list(),
        history = list(),
        getHistory = function() return(nc$history),
        getNumMessagesSent = function() return(length(nc$history))
    )
    #Add a few more methods
    nc$sendMail = function(to) {
        cat(paste("Sending mail to", to, 'from', nc$email))
        h <- nc$history
        h[[(length(h)+1)]] <- list(to=to, timestamp=Sys.time())
        assign('history', h, envir=nc)
    }
    nc$addProp = function(name, value) {
        p <- nc$props
        p[[name]] <- value
        assign('props', p, envir=nc)
    }
    nc <- list2env(nc)
    class(nc) <- "EmailClass"
    return(nc)
}

#' Define S3 generic method for the print function.
print.EmailClass <- function(x) {
    if(class(x) != "EmailClass") stop();
    cat(paste(x$get("name"), "'s email address is ", x$get("email"), sep=''))
}

Et un code de test:

    test <- EmailClass(name="Jason", "[email protected]")
    test$addProp('hello', 'world')
    test$props
    test
    class(test)
    str(test)
    test$get("name")
    test$get("email")
    test$set("name", "Heather")
    test$get("name")
    test
    test$sendMail("[email protected]")
    test$getHistory()
    test$sendMail("[email protected]")
    test$getNumMessagesSent()

    test2 <- EmailClass("Nobody", "[email protected]")
    test2
    test2$props
    test2$getHistory()
    test2$sendMail('[email protected]')

Voici un lien vers un article de blog que j'ai écrit sur cette approche: http://bryer.org/2012/object-oriented-programming-in-r J'accueillerais volontiers vos commentaires, critiques et suggestions à cette approche car je ne suis pas moi-même convaincu si c'est la meilleure approche. Cependant, pour le problème que j'essayais de résoudre, il a très bien fonctionné. Plus précisément, pour le package makeR ( http://jbryer.github.com/makeR ), je ne voulais pas que les utilisateurs modifient directement les champs de données car je devais m'assurer qu'un fichier XML représentant l'état de mon objet resterait synchronisé. Cela a fonctionné parfaitement tant que les utilisateurs respectent les règles que je décris dans la documentation.

14
jbryer