Programmer en R/Programmer une fonction

Fonctions définies par l'utilisateur

modifier

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

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

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.

Récursivité

modifier

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