Aller au contenu

Programmer en R/Programmer une fonction

Un livre de Wikilivres.

Fonctions définies par l'utilisateur

[modifier | modifier le wikicode]

Le logiciel R dispose de fonctions préprogrammées, appelées « primitives ». L'utilisateur a la possibilité de définir ses propres fonctions.

Une fonction est un sous-programme, c'est-à-dire une portion de code qui est exécutée lorsqu'on l'appelle.

Le logiciel R crée un environnement spécifique pour l'exécution de la fonction, c'est-à-dire qu'il travaille avec des variables locales. Ainsi, une variable définie dans une fonction peut avoir le même nom qu'une variable générale, ce sont deux variables indépendantes ; on peut modifier la valeur de cette variable, mais elle retrouve sa valeur initiale lorsque l'on sort de la fonction.

Définition d'une fonction

[modifier | modifier le wikicode]

Une fonction est mise dans une variable contenant un bloc d'instructions introduit par la commande function(). La syntaxe générale est :

nom_de_fonction <- function(arguments) {
    instructions
}

par exemple

> carre <- function(x) x*x
> carre(2)
[1] 4

L'évaluation de la fonction renvoie la dernière valeur calculée. On peut préciser la valeur retournée par la commande return() :

nom_de_fonction <- function(arguments) {
    instructions
    return(valeur)
}

par exemple

carre <- function(x) {
    y <- x*x
    return(y)
}

Fonctions sur les vecteurs

[modifier | modifier le wikicode]

Les fonctions mathématiques primitives de R s'appliquent sur des vecteurs. On s'attachera donc à créer des fonctions qui s'appliquent elles-mêmes à des vecteurs. Par exemple, la fonction suivante permet de définir une courbe en cloche dissymétrique, formée de deux demies gaussiennes de largeur différentes.

gauss_dissym <- function(A, x) {
  # génère un pic gaussien dissymétrique
  # entrées : A : vecteur de réels, paramètres de la fonction
  #   A[1] : position du pic
  #   A[2] : hauteur de la courbe
  #   A[3] : largeur de la courbe à gauche
  #   A[4] : largeur de la courbe à droite
  #   x : vecteur de réels
  # sorties : y : vecteur de réels
  indice <- (x < A[1]) # vecteur de T à gauche, F à droite
  y <- rep(0, length(x)) # initialisation par des zéros
  y[indice] <- A[2]*exp(-(x[indice] - A[1])^2/A[3]) # profil gauche
  y[!indice] <- A[2]*exp(-(x[!indice] - A[1])^2/A[4]) # profil droit
  return(y)
}

Le fait d'utiliser la matrice de booléens indice permet, au sein d'une seule fonction, de séparer les cas x < A[1] et x ≥ A[1]. On peut donc utiliser cette fonction sur un vecteur :

x <- seq(-5, 5, len=100)
A <- c(1, 1, 2, 5)
y <- gauss_dissym(A , x)
plot(x, y, "l")

Si l'on n'arrive pas à faire autrement, on peut toujours faire défiler les indices avec une boucle, mais l'évaluation de la fonction est alors plus lente.

Le langage S est un langage récursif. Une fonction définie dans un script R peut donc s'appeler elle-même, avec la précaution d'usage : il faut prévoir une condition d'arrêt. Comme dans tous les langages récursifs, R crée un environnement spécifique pour l'exécution de la fonction (variables locales), il « empile » les différents appels, puis les « dépile » lorsque la condition d'arrêt est atteinte.

Nous illustrons ceci par le codage récursif de la fonction factorielle.

factorielle <- function(n) {
  if (n==1) resultat <- 1 # arrêt de la récursion
  else resultat <- factorielle(n-1)*n # appel récursif
  return(resultat)
}

Mais nous remarquons que cette fonction ne s'applique qu'aux scalaires, en raison de la présence du test if (n == 1) : la condition if ne s'applique que sur un scalaire booléen. On peut modifier le code pour le rendre exécutable sur les vecteurs :

factorielle <- function(n) {
  indice <- (n == 1)
  if (all(indice)) return(n) # arrêt de la récursion
  n[!indice] <- n[!indice]*factorielle(n[!indice] - 1) # appel récursif
  return(n)
}

Comme souvent, on crée un vecteur de booléens appelé indice. Si toutes les valeurs sont à « 1 », alors on retourne le vecteur lui-même (puisque 1! = 1) ; c'est l'arrêt de la récursion. Sinon, on extraie le sous-vecteur dont les valeurs ne sont pas « 1 », et l'on applique la récursion.

On peut le tester avec par exemple

> x = c(1:5, 1:5)
> print(x)
 [1] 1 2 3 4 5 1 2 3 4 5
> factorielle(x)
 [1]   1   2   6  24 120   1   2   6  24 120