5  Fonctions

Les fonctions sont généralement abordées plus tard mais je crois non seulement en vous mais aussi qu’elles peuvent et doivent être démystifiées précocément. Nous allons commencer par la partie créative et écrire notre première fonction.

5.1 Une première fonction

Nous pouvons définir nos propres fonctions en utilisant la fonction function.

Nous allons ensuite encapsuler une portion de code entre des accolades { et l’assigner comme tout autre objet R.

Définissons une fonction qui ajoute 3 à un argument que l’on va appeller x (vous pouvez essayer avec y ou toto :

plus3 <- function(x) {
  x+3
}

plus3(5)
[1] 8
plus3(1:3)
[1] 4 5 6

Les fonctions permettent de ne pas copier-coller bêtement du code, de dupliquer des lignes. Dès que vous répétez un bout de code, vous pouvez profitablement en faire une fonction.

À terme, cela vous permettra d’avoir du code non dupliqué, moins propice à des erreurs de frappes.

Aussi, si vous changez d’avis, vous pourrez changer la définition de fonction et, chaque fois qu’elle sera appelée en aval, le nouveau comportement sera appliqué. Nous reviendrons sur ces bonnes pratiques à la toute fin de cette formation.

L’expérience vous guidera sur quand et comment créér des fonctions à la bonne granularité, c’est à dire les plus génériques possibles mais dans les limites du bon sens.

5.2 Documentation des fonctions

Les fonctions sont des unités de code qui font quelque chose d’utile. Le plus souvent on envoie une valeur, un objet, et on en récupère une autre mais certaines produisent quelque chose “ex nihilo” sqrt() par exemple renvoie la racine carrée de la valeur passée en argument.

Les arguments, séparés par des virgules, définissent les “options” de la fonction concernée. Une fonction peut avoir zéro, un, plusieurs et même un nombre indéfini d’argument.

Toute fonction déjà disponible en R ou un package a forcément une page d’aide dédiée à laquelle on accède avec : ?nom_de_la_fonction.

Revenons à notre fonction seq bien pratique pour créer des sequences régulières. En tapant ?seq on accède au contenu suivant :

Sequence Generation

Description
Generate regular sequences. seq is a standard generic with a default method. seq.int is a primitive which can be much faster but has a few restrictions. seq_along and seq_len are very fast primitives for two common cases.

## Default S3 method:
seq(from = 1, to = 1, by = ((to - from)/(length.out - 1)),
    length.out = NULL, along.with = NULL, ...)

seq.int(from, to, by, length.out, along.with, ...)

seq_along(along.with)
seq_len(length.out)

Arguments
[...]

Details
[...]

Value
[...]

See also
[...]

Examples
[...]

Toutes les pages d’aides ont la même structure et possèdent les mêmes sections. Regardons-les de plus près :

  • Description : ce que la fonction fait
  • Usage : la fonction “déployée” c’est à dire avec tous ses arguments. Parfois plusieurs fonctions sont regroupées dans une même age d’aide, comme c’est le cas pour seq. Ces fonctions peuvent être des variantes avec des noms différents ou des méthodes c’est à dire des fonctions au comportement différent selon le type d’objet sur lequel elles opèrent.
  • Arguments : un descriptif de tous les arguments disponibles. La classe et le format de chacun d’eux est mentionnée.
  • Details : souvent un remède à l’insomnie mais les subtilités d’implémentation sont là, souvent cachées au détour d’une phrase.
  • Value : ce que la fonction retourne.
  • References : où s’en référer si vous n’en avez pas assez
  • See Also : fonctions connexes, très pratique pour enrichir son vocabulaire et trouver son bonheur.
  • Examples : peut être la plus utile de toutes avec ses exemples d’utilisation que vous pouvez copier-coller ou même appeller directement depuis la console avec example("nom_de_la_fonction"). Vous pouvez essayer example("plot") par exemple.
  • En pied de page, vous avez également une information qui sera utile plus tard : le package et sa version dans lequel se trouve être cette fonction. Pour seq, on est dans le package base dont toutes les autres fonctions sont indexées dans le lien “Index”.

Certaines pages d’aide, surtout pour le langage lui-même, sont plutôt des résumés du fonctionnement et sont un peu moins intuitives à trouver, par exemple ?Arithmetic, ?Special, Syntax.

D’autres fonctions, par exemple pour les opérateurs, doivent être encadrées de guillemets arrières (`), par exemple ?`+` ou la mise en abyme de ?`?`. Enfin, il existe d’autres ressources comme les “vignettes”, plus conviviales, surtout pour les packages les plus récents. Nous y reviendrons.

Les pages d’aide sont souvent compactes et obscures mais l’information que vous cherchez est probablement là. On apprend beaucoup à lire ces pages d’aide même si à première vue cette littérature n’est guère attrayante.

Enfin, la variante ??(quoiquoiquoi), raccourci de help.search("quoiquoiquoi") permet de chercher toutes les occurences de quoiquoiquoi dans toutes les pages d’aide de R.

5.3 Arguments : paramétrer le comportement des fonctions

Après avoir consulté ?seq on peut par exemple préciser le point de départ (from), le point d’arrivée (to), le pas (by) et la longueur totale du vecteur à créer (length.out).

Vous constaterez que from est défini avec une valeur par défaut (from=1). Ainsi, si vous omettez sa valeur et ne spécifiez que to, from prendra sa valeur par défaut. Ces deux commandes sont donc équivalentes :

seq(from=1, to=5)
[1] 1 2 3 4 5
seq(to=5)
[1] 1 2 3 4 5

Vous pouvez également abréger le nom des arguments, moyennant que l’abbréviation soit univoque, c’est à dire que le nom de l’argument que vous abrégez ne soit pas identique à celui d’un autre argument. Ainsi from et length.out peuvent être abrégés en fr et length:

seq(fr=0, to=2, length=5)
[1] 0.0 0.5 1.0 1.5 2.0

Ce n’est jamais une bonne idée mais si vous utilisez le nom complet ou une abbréviation des arguments vous pouvez changer leur ordre. Ainsi seq(length=5, to=2, fr=0) sera équivalent à la commande précédente.

Vous pouvez même omettre le nom des arguments comme on l’a fait dans les sections précédentes sans le mentionner. Dans ce cas, les arguments sont passés positionnellement et doivent être renseignés dans l’ordre tel que défini dans la section ‘Usage’ de leur documentation:

seq(-3, 4, 12)
[1] -3

5.4 Une deuxième fonction

Si vous avez bien compris la section précédent, alors le deuxième exemple avec deux arguments, et des valeurs par défaut, devrait vous paraître limpide :

add_this <- function(x=1, y=0){
  x+y
}

add_this()
[1] 1
add_this(5)
[1] 5
add_this(y=7)
[1] 8
add_this(2, 10)
[1] 12

5.5 Accéder au code interne des fonctions

Le code des fonctions est toujours accessible, le plus souvent simplement en tapant le nom des fonctions, sans parenthèses.

plus3
<srcref: file "" chars 1:10 to 3:1>
<bytecode: 0x7fe69f525a40>
add_this
<srcref: file "" chars 1:13 to 3:1>
<bytecode: 0x7fe6a1219d10>

Parfois le code est lové dans des Primitives (des fonctions non écrites en R) ou bien dans des méthodes (des fonctions qui changent de comportement suivant le type d’objet qu’on leur passe). Nous y reviendrons.

c
function (...)  .Primitive("c")
seq
function (...) 
UseMethod("seq")
<bytecode: 0x7fe69aad9460>
<environment: namespace:base>

❤ Placé dans le domaine public par Vincent Bonhomme