Imprimer
Catégorie : Structure de la programmation R

Les instructions de contrôle dans R ressemblent beaucoup à celles des langages de la famille descendante ALGOL mentionnés ci-dessus. Ici, nous examinerons les boucles et les instructions if-else.


1 Boucles
Dans la section 1.3, nous avons défini la fonction oddcount (). Dans cette fonction, la ligne suivante aurait dû être immédiatement reconnue par les programmeurs Python:

  1. for (n in x) {
Source code
Cela signifie qu'il y aura une itération de la boucle pour chaque composante du vecteur x, avec n prenant les valeurs de ces composantes dans la première itération, n = x [1]; dans la deuxième itération, n = x [2]; etc. Par exemple, le code suivant utilise cette structure pour sortir le carré de chaque élément d'un vecteur:

  1. > x <- c(5,12,13)
  2. > for (n in x) print(n^2)
  3. [1] 25
  4. [1] 144
  5. [1] 169
Source code
La boucle de style C avec while et repeat est également disponible, avec break, une instruction qui fait que le contrôle quitte la boucle. Voici un exemple qui utilise tous les trois:

  1. > i <- 1
  2. > while (i <= 10) i <- i+4
  3. > i
  4. [1] 13
  5. >
  6. > i <- 1
  7. > while(TRUE) { # similar loop to above
  8. + i <- i+4
  9. + if (i > 10) break
  10. + }
  11. > i
  12. [1] 13
  13. >
  14. > i <- 1
  15. > repeat { # again similar
  16. + i <- i+4
  17. + if (i > 10) break
  18. + }
  19. > i
  20. [1] 13
Source code

Dans le premier fragment de code, la variable i a pris les valeurs 1, 5, 9 et 13 comme la boucle a traversé ses itérations. Dans ce dernier cas, la condition i <= 10 a échoué, alors la pause s'est installée et nous avons quitté la boucle.
Ce code montre trois façons différentes d'accomplir la même chose, la pause jouant un rôle clé dans les deuxième et troisième façons.
Notez que repeat n'a pas de condition de sortie booléenne. Vous devez utiliser break (ou quelque chose comme return ()). Bien sûr, break peut aussi être utilisé avec les boucles for.
Une autre instruction utile est la suivante, qui demande à l'interpréteur de passer le reste de l'itération en cours de la boucle et de passer directement au suivant. Cela permet d'éviter d'utiliser des constructions if-then-else complexes, ce qui peut rendre le code déroutant. Jetons un oeil à un exemple qui utilise ensuite. Le code suivant provient d'un examen étendu du chapitre 8:

  1. 1 sim <- function(nreps) {
  2. 2 commdata <- list()
  3. 3 commdata$countabsamecomm <- 0
  4. 4 for (rep in 1:nreps) {
  5. 5 commdata$whosleft <- 1:20
  6. 6 commdata$numabchosen <- 0
  7. 7 commdata <- choosecomm(commdata,5)
  8. 8 if (commdata$numabchosen > 0) next
  9. 9 commdata <- choosecomm(commdata,4)
  10. 10 if (commdata$numabchosen > 0) next
  11. 11 commdata <- choosecomm(commdata,3)
  12. 12 }
  13. 13 print(commdata$countabsamecomm/nreps)
  14. 14 }
Source code
Il y a les prochaines déclarations aux lignes 8 et 10. Voyons comment ils fonctionnent et comment ils améliorent les alternatives. Les deux instructions suivantes se trouvent dans la boucle qui commence à la ligne 4. Ainsi, lorsque la condition if se trouve à la ligne 8, les lignes 9 à 11 seront ignorées et le contrôle sera transféré à la ligne 4. La situation de la ligne 10 est similaire.
Sans utiliser ensuite, nous aurions besoin de recourir à des instructions imbriquées if, quelque chose comme ceci:

  1. 1 sim <- function(nreps) {
  2. 2 commdata <- list()
  3. 3 commdata$countabsamecomm <- 0
  4. 4 for (rep in 1:nreps) {
  5. 5 commdata$whosleft <- 1:20
  6. 6 commdata$numabchosen <- 0
  7. 7 commdata <- choosecomm(commdata,5)
  8. 8 if (commdata$numabchosen == 0) {
  9. 9 commdata <- choosecomm(commdata,4)
  10. 10 if (commdata$numabchosen == 0)
  11. 11 commdata <- choosecomm(commdata,3)
  12. 12 }
  13. 13 }
  14. 14 print(commdata$countabsamecomm/nreps)
  15. 15 }
Source code
Parce que cet exemple simple n'a que deux niveaux, ce n'est pas trop mal. Cependant, les instructions imbriquées peuvent devenir déroutantes lorsque vous avez plus de niveaux.
La construction for fonctionne sur n'importe quel vecteur, quel que soit le mode. Vous pouvez faire une boucle sur un vecteur de noms de fichiers, par exemple. Disons que nous avons un fichier nommé file 1 avec le contenu suivant:
1
2
3
4
5
6
Nous avons aussi un fichier nommé file 2 avec ces contenus:
5
12
13
La boucle suivante lit et imprime chacun de ces fichiers. Nous utilisons ici la fonction scan () pour lire dans un fichier de nombres et stocker ces valeurs dans un vecteur. Nous parlerons plus en détail de scan () au chapitre 10.

  1. > for (fn in c("file1","file2")) print(scan(fn))
  2. Read 6 items
  3. [1]123456
  4. Read 3 items
  5. [1] 51213
Source code
Donc, fn est d'abord réglé sur filel, et le fichier de ce nom est lu et imprimé. Ensuite, la même chose se produit pour file2.


2 Bouclage sur des ensembles non-vecteurs
R ne supporte pas directement l'itération sur les ensembles non-vecteurs, mais il existe quelques moyens indirects mais faciles à réaliser:
• Utilisez lapply (), en supposant que les itérations de la boucle sont indépendantes les unes des autres, ce qui permet de les exécuter dans n'importe quel ordre.

 

• Utilisez get (). Comme son nom l'indique, cette fonction prend comme argument une chaîne de caractères représentant le nom d'un objet et renvoie l'objet de ce nom. Cela semble simple, mais get () est une fonction très puissante.
Regardons un exemple d'utilisation de get (). Supposons que nous ayons deux matrices, u et v, contenant des données statistiques, et nous souhaitons appliquer à chacun d'eux la fonction de régression linéaire lm ().

  1. >u
  2. [,1] [,2]
  3. [1,] 1 1
  4. [2,] 2 2
  5. [3,] 3 4
  6. >v
  7. [,1] [,2]
  8. [1,] 8 15
  9. [2,] 12 10
  10. [3,] 20 2
  11. > for (m in c("u","v")) {
  12. + z <- get(m)
  13. + print(lm(z[,2] ~ z[,1]))
  14. +}
  15. Call:
  16. lm(formula = z[, 2] ~ z[, 1])
  17. Coefficients:
  18. (Intercept) z[, 1]
  19. -0.6667 1.5000
  20. Call:
  21. lm(formula = z[, 2] ~ z[, 1])
  22. Coefficients:
  23. (Intercept) z[, 1]
  24. 23.286 -1.071
Source code
Ici, m a été mis pour la première fois. Alors ces lignes assignent la matrice u à z, ce qui permet l'appel à lm () sur u:

  1. z <- get(m)
  2. print(lm(z[,2] ~ z[,1]))
Source code
La même chose se produit alors avec v.


3 if-else
La syntaxe de if-else ressemble à ceci:

  1. if (r == 4) {
  2. x<-1
  3. } else {
  4. x<-3
  5. y<-4
  6. }
Source code
Cela semble simple, mais il y a ici une subtilité importante. La section if se compose d'une seule déclaration:
  1. x <- 1
Source code
Donc, vous pourriez deviner que les accolades autour de cette déclaration ne sont pas nécessaires. Cependant, ils sont en effet nécessaires.
L'accolade droite avant le else est utilisée par l'analyseur R pour déduire qu'il s'agit d'un if-else plutôt que d'un if. En mode interactif, sans accolades, l'analyseur pense par erreur à ce dernier et agit en conséquence, ce qui n'est pas ce que nous voulons.
Une instruction if-else fonctionne comme un appel de fonction et, à ce titre, renvoie la dernière valeur attribuée.

  1. v <- if (cond) expression1 else expression2
Source code
Cela mettra v au résultat de l'expression! ou expression2, selon que la condition est vraie. Vous pouvez utiliser ce fait pour compacter votre code. Voici un exemple simple:

  1. >x<-2
  2. > y <- if(x == 2) x else x+1
  3. >y
  4. [1] 2
  5. >x<-3
  6. > y <- if(x == 2) x else x+1
  7. >y
  8. [1] 4
Source code

Sans prendre ce point, le code

  1. y <- if(x == 2) x else x+1
Source code
consisterait plutôt en un peu plus encombré

  1. if(x == 2) y <- x else y <- x+1
Source code
Dans des exemples plus complexes, expression! et / ou expression2 pourraient être des appels de fonction. D'un autre côté, vous ne devriez probablement pas laisser la compacité prendre le pas sur la clarté.
Lorsque vous travaillez avec des vecteurs, utilisez la fonction ifelse (), comme indiqué au chapitre 2, car elle produira probablement un code plus rapide.