Compléments sur les bases de PHP

Licence Informatique, semestre 6

Alexandre NiveauJean-Marc Lecarpentier — Ludovic Jean-Baptiste

Enseignement des technologies du Web

 

Compléments sur les bases de PHP

Notes de cours

Travail personnel

Objectifs

On continue à manipuler PHP pour prendre de bonnes habitudes.

Si vous êtes en difficulté, le TP sera certainement trop long pour vous : ne cherchez pas à finir à tout prix le premier exercice avant de passer au suivant, essayez de faire moitié-moitié par exemple.

Exercice 1 — Analyse d’un fichier CSV #

Dans cet exercice on va manipuler un fichier CSV avec des données sur les prénoms donnés en France. Ce fichier est mis à disposition sur le portail des données ouvertes du gouvernement.

  1. Télécharger le fichier des données par département de naissance (au cas où, je mets le lien du fichier sur le portail). Décompressez l'archive et placez le fichier CSV quelque part.
  2. Regarder un peu le contenu du fichier (attention, il fait 75 Mo : vous pouvez utiliser par exemple less en ligne de commande pour que ça reste fluide), et comprendre le contenu. Par exemple, la ligne 1;AARON;2005;14;4 signifie qu'en 2005, dans le Calvados, sont nés quatre garçons qui ont été prénommés Aaron. Voir aussi la doc sur le site.
  3. Créer un script prenoms.php, et lire le fichier : commencer par afficher toutes les lignes une par une.
  4. Construire un tableau de toutes les données : il doit contenir, pour chaque ligne, un tableau associatif avec comme clefs sexe, prenom, annee, departement, nombre.
  5. Utiliser ce tableau pour répondre aux questions suivantes. (NB : les fonctions map/filter montrées en cours peuvent être pratiques…)
    1. Combien d'enfants sont nés (en France) sur la période ?
    2. Combien d'enfants sont nés dans le Calvados (14) sur la période ?
    3. Combien d'enfants ont été appelés David sur la période ?
    4. Combien d'enfants ont été appelés Zeinab en 2012 ?
    5. Quel était le prénom féminin le plus fréquent dans la Manche (50) en 1978 ?
    6. Quel était le prénom féminin le plus fréquent dans toute la France en 1996 ?
    7. Quel prénom a été le plus donné sur la période ?
    8. Combien d'enfants ont eu un nom commençant par un ou plusieurs « A » et se terminant par un ou plusieurs « Z » ?
    9. Combien de prénoms différents le fichier contient-il ?
    10. Combien y a-t-il eu de naissances à la Guadeloupe (971) sur la période ? NB: les données ne permettent de répondre qu'approximativement à cette question, car les prénoms donnés moins de 3 fois dans une année sont rassemblés dans l'année spéciale XXXX
    11. Quel département a la plus grande diversité de prénoms (ratio du nombre de prénoms distincts donnés sur la période et du nombre de naissances) ?

Exercice 2 (optionnel) — Rule 110, un automate cellulaire élémentaire (version procédurale) #

Si vous êtes en retard sur les TP, ne faites pas cet exercice : il est repris en version objet dans le TP suivant.

Dans cet exercice on va programmer l'automate cellulaire élémentaire appelé « rule 110 » (un des systèmes les plus simples qui soit Turing-universel, c'est-à-dire capable de simuler n'importe quel calcul).

Contexte

Automate cellulaire élémentaire

On peut voir un automate cellulaire élémentaire comme la simulation d'un monde très simple. Ce monde est constitué de cellules, qui n'ont que deux états possibles, blanc ou noir (ou morte/vivante, ou vide/pleine, ou fausse/vraie… comme vous voulez). Elles sont organisées sur une ligne (le monde n'a donc qu'une seule dimension). En général on considère que les deux extrémités de la ligne se rejoignent — il s'agit donc plutôt d'un cercle, mais ce n'est pas très important ici.

À chaque pas de temps (ou à chaque « génération »), les cellules évoluent : une cellule blanche peut devenir noire et inversement, et ce, en fonction de son état et de l'état de ses deux cellules voisines, suivant une règle établie au départ. Un exemple de règle : une cellule se retrouve noire si, au pas de temps précédent, parmi elle et ses deux voisines il y avait un nombre impair de cellules noires (cette règle est appelée « rule 150 »). En fonction de la règle d'évolution, l'automate se comporte de manière drastiquement différente ; certaines règles donnent des résultats très répétitifs, d'autres complètement chaotiques.

Rule 110
illustration animée du fonctionnement de la règle 110
Illustration animée du fonctionnement de la règle 110. En haut, l'état du monde ; au milieu, la règle d'évolution ; en bas, la génération suivante. Source : Wikimedia Commons, CC-BY-SA

La règle 110 n'est pas très compliquée : une cellule ne peut se retrouver blanche que dans trois cas

  • si elle et ses voisines étaient toutes trois blanches
  • si elle et ses voisines étaient toutes trois noires
  • si sa voisine de gauche était noire, mais elle-même et sa voisine de droite étaient blanches

Cette règle donne des résultats très intéressants, car à la fois réguliers et chaotiques : de fait, l'automate correspondant est capable de simuler l'exécution de n'importe quel programme (en encodant le programme comme un état initial du monde, et en lisant le résultat dans un état considéré comme final).

Implémentation

Après ces éléments de contexte, passons à l'implémentation. On va écrire un script rule-110.php.
Représentation du monde

On va représenter le monde comme un tableau de booléens, true représentant une cellule noire (vivante, présente) et false une cellule blanche (morte, absente).

  1. Écrire une fonction build_world qui renvoie un monde avec 100 cellules, toutes blanches, sauf quatre ou cinq (mettez-les où vous voulez).
  2. Écrire une fonction print_world qui affiche un monde. On pourra utiliser un # (ou le caractère « FULL BLOCK » █) pour les cellules noires.
Évolution
  1. Écrire une fonction compute_next_state_rule110($left, $self, $right), qui renvoie l'état d'une cellule à la prochaine génération, en fonction de son propre état (paramètre $self) et de celui de ses deux voisines ($left et $right).
  2. Écrire une fonction next_generation($world), qui renvoie la prochaine génération du monde qu'on lui a passé en paramètre. Pour simplifier, on pourra considérer que le monde est une ligne, et ignorer les cellules aux extrémités.
  3. Écrire une fonction display_evolution($world, $nb_generations), qui affiche $nb_generations successives du monde passé en paramètre.
  4. Afficher 50 générations du monde construit par build_world. Vérifiez que ça marche ! (La règle est-elle bien respectée à vue de nez ? Est-ce que ça ressemble aux dessins sur Wikipédia ?)

Améliorations

Maintenant que le programme fonctionne, on va l'améliorer.

  1. Notre fonction build_world n'est pas terrible. Modifiez-la pour respecter les contraintes suivantes.
    1. La taille du monde doit être passée en paramètre.
    2. Le contenu du tableau doit être aléatoire.
    3. Ajouter un paramètre pour contrôler la proportion moyenne de cellules noires. Par exemple, si on passe 0.75, alors chaque cellule doit avoir une probabilité 0.75 d'être noire.
  2. Pour pouvoir facilement observer les résultats, on va passer des paramètres au programme lui-même. Dans PHP, la variable $argv est un tableau contenant chacun des arguments au script (ainsi que le nom du script, dans la case d'indice 0).
    1. Faire en sorte qu'on puisse passer le ratio de cellules noires, la taille du monde, et le nombre de générations voulus en paramètres du script. Exemple d'appel : php rule-110.php 0.2 150 40
    2. Faire en sorte que ces paramètres soient optionnels (avec des valeurs par défaut convenables).
    Testez un peu avec des ratios bas, très bas, hauts…
  3. Le fait qu'on ignore les extrémités du monde n'est pas très satisfaisant (et cela peut générer des effets de bord (au sens propre)). Faire en sorte que le monde soit circulaire, c'est-à-dire que la voisine de gauche de la première cellule soit en fait la dernière (et réciproquement). NB : chercher à conserver un code élégant, sans cas particuliers ! Une solution peut être de définir une fonction du genre get_cell_at_index($world, $index), où on autorise $index à sortir du tableau, à droite comme à gauche.
  4. Ajouter à next_generation un paramètre $compute_next_state, qui va contenir une fonction. Modifier ensuite le code pour que ce soit cette fonction qui soit appelée au lieu de compute_next_state_rule110. Vérifier que ça marche toujours si on passe 'compute_next_state_rule110' comme paramètre (NB : pour passer comme paramètre de fonction, une fonction qui existe déjà, il faut passer son nom sous forme de chaîne de caractère.)
  5. Implémenter une autre règle, par exemple la règle 184, et afficher le résultat. Voir ici des dessins d'un bon nombre d'autres règles.
  6. Optionnel : écrire une fonction create_rule($rule_number) qui prend un nombre entre 0 et 254 en paramètre, et renvoie une fonction de trois paramètres qui implémente la règle ayant ce numéro. Par exemple, create_rule(110) devrait renvoyer une fonction qui fait exactement la même chose que notre fonction compute_next_state_rule110. La signification du numéro peut être trouvée sur Wikipédia.