Advent of Code, c’est un calendrier de l’avent d’exercices de programmation. Chaque jour un nouveau, et la difficulté monte progressivement. C’est complètement gratuit, il suffit de créer un compte pour y participer. Chacun récupère un jeu de données qui lui est propre chaque jour et doit donc soumettre LA réponse associée, qui est toujours une valeur numérique à trouver. Chaque jour propose 1 exercice décomposé en 2 niveaux de difficulté, un abordable, et un autre beaucoup + compliqué… Le choix du langage est libre puisqu’on récupère simplement un input au format texte. Il faudra donc résoudre le problème en local.

A la conquête des étoiles !

Dédicaces

C’est la première année que je le faisais assidûment, 1 heure (voir +) presque chaque soir de décembre pour venir à bout de l’exercice du jour. Pas toujours facile, mais la motivation était entretenue par d’autres développeurs qui se sont prêtés au jeu et ont partagé leur solution quotidiennement :

Jour préféré

Mon « jour préféré » aura été le jour 11, un exercice qui a une complexité « abordable », pas besoin de connaître tel ou tel algorithme ou théorie mathématique pour le résoudre. Mais il est très complet et il faudra même passer par un peu de récursivité pour le résoudre ! Donc si vous deviez tester un exercice, testez celui la : Advent of Code 2020, jour 11.

Enseignements et bonnes pratiques PHP

Sélection d’éléments que j’ai découverts ou perfectionné au cours de ces challenges, en grande partie grâce aux 3 personnes mentionnées + haut.

Le typage en PHP

Bon, ça existe depuis longtemps mais un rappel ne fait pas de mal. Comme j’ai commencé à coder sans typage en PHP, je n’étais pas toujours rigoureux sur cet aspect. Mais maintenant je ne m’en passe plus. En PHP, il est possible de typer :

  • Les paramètres d’une fonction ou méthode
  • Le retour d’une fonction ou méthode
  • Les propriétés d’une classe

Exemple :

<?php
function calcul(array $data): int
{
	// Fait des calculs sur les données contenues dans un tableau $data
	// Retourne un entier
}

function testFormat(string $password): bool
{
	// Teste le format de la chaîne de caractères $password
	// Retourne un booléen
}

final class Hero
{
	private int $force;
	// La propriété $force est un entier

	public function __construct(int $startForce = 10)
	{
		$this->force = $startForce;
	}
	// Pas de type de retour pour le constructeur

	public function addForce(int $value): void
	{
		$this->force += $value;
	}
	// "void" permet de préciser que la méthode ne retourne rien
}

On pourrait se demander « mais à quoi ça sert de typer » ? Typer vous permettra de renforcer et sécuriser votre code. En effet, PHP déclenche une erreur fatale si le typage n’est pas respecté. Pour une méthode, plus besoin de tester la nature du ou des paramètres, PHP le fera directement. Si vous structurez une fonction avec un type de retour précis attendu, si la fonction évolue et que ce retour vient à changer, pareil, PHP déclenche une erreur fatale.

Ce n’est ici qu’un avant-goût de ce qu’il est possible de faire avec le typage, n’hésitez pas à vous référer à la documentation officielle pour plus d’informations.


Respecter les protections dans les classes

Là il est question d’une bonne pratique. Comme pour le typage, cette bonne pratique vous permet de renforcer et sécuriser votre code.

Vous créez une nouvelle classe, déclarez-la comme « final ». Vous créez des propriétés ou des méthodes, déclarez-les comme « private ».

Et vous ne changez que si vous en avez besoin, de cette manière, vous avez une classe qui est toujours sécurisée.

Pour l’exemple, vous pouvez vous référer à la classe Hero juste au-dessus :

  • Il n’y a (pour le moment) pas de classe qui hérite de Hero, je laisse donc « final »
  • La propriété $force est réservée à la classe, donc elle reste « private »
  • Mon constructeur est « public »
  • Ma méthode « addForce » doit pouvoir être appelée en dehors de ma classe, je l’ai donc passé, après l’avoir déclarée initialement comme « private », en « public »

Comparaison stricte

Même registre, favorisez les comparaisons strictes : « === » ou « !== ». Cela permet d’inclure la correspondance du type dans la comparaison. Et donc de bien comparer des choses semblables.

Exemples :

<?php
$sentence = 'Bryan is in the kitchen';
if (strpos($sentence, 'Bryan') != false) {
	echo 'Bryan is here';
}

// Ce code n'affiche rien

strpos renvoie la position de « Bryan » dans $sentence et va renvoyer false s’il ne trouve pas « Bryan ». Comme « Bryan » est au début, strpos renvoie 0. Et en PHP, 0 et false, dans une opération booléenne, c’est la même chose ! Du coup, il n’y a pas d’affichage. Il faut donc écrire :

$sentence = 'Bryan is in the kitchen';
if (strpos($sentence, 'Bryan') !== false) {
	echo 'Bryan is here';
}

// Ce code affiche bien "Bryan is here"

Avec donc « !== » au lieu de simplement « != ». Ici PHP va faire une comparaison stricte, en impliquant le type. Et du coup 0 n’est plus la même chose que false.

Encore une fois, en utilisant la comparaison stricte 100% du temps, vous renforcez et sécurisez votre code. Certaines erreurs (comme celle présentée dans l’exemple) seront aussi plus faciles à identifier.


Se passer des « if »

J’ai déjà parlé de se passer des else en programmation. Mais il s’avère qu’on peut aussi se passer des if !

Dans Advent of Code, il était souvent question de boucle et de compteur, prenons le code (simplifié) suivant :

<?php
// Version classique, avec un if
$count = 0;
foreach ($data as $value) {
	if ($value === CONTROL_VALUE) {
		$count++;
	}
}

// Avec un if ternaire
$count = 0;
foreach ($data as $value) {
	$count += ($value === CONTROL_VALUE) ? 1 : 0;
}

// Sans if !
$count = 0;
foreach ($data as $value) {
	$count += (int) ($value === CONTROL_VALUE);
}

Selon le bon respect de ma condition, je dois incrémenter de 1. Du coup, je peux transformer le résultat de ma comparaison en entier et l’incrémenter à $count. true devient 1, j’incrémente de 1. false devient 0, j’incrémente de 0, $count ne change donc pas.

Encore une fois, on a joué avec le typage, décidemment !


La fonction « sscanf »

La découverte de cette fonction a changé ma vie !

Dans l’exercice du jour 4, il fallait traiter ce genre de chaînes de caractères : « eyr:2037 ».

<?php
// Avant sscanf, avec ce bon vieu explode
$data = explode(':', $str);
$code = $data[0];
$value = $data[1];

// Avec sscanf
[$code, $value] = sscanf($str, '%3c:%d');

sscanf permet donc de décrire le format de la chaîne de caractères à décortiquer. Et avec cette logique d’attribution multiple, plus besoin de manipuler « $data[0] » ou autres, qui ont très peu de sens sémantiquement parlant.

La documentation officielle de sscanf.


Multichangement de valeurs

Un des premiers exercices d’algorithmie consiste à inverser les valeurs de 2 variables, en créant donc 1 variable intermédiaire. Si on doit intervertir les valeurs de + de variables, ça peut devenir + lourd :

<?php
// Avec 2 variables
$var1 = 12;
$var2 = 36;

$var1Tmp = $var1;

$var1 = $var2;
$var2 = $var1Tmp;

// Avec 4 variables
$var1 = 12;
$var2 = 36;
$var3 = 48;
$var4 = 100;

$var1Tmp = $var1;
$var2Tmp = $var2;
$var3Tmp = $var3;
$var4Tmp = $var4;

$var1 = $var4Tmp;
$var2 = $var3Tmp;
$var3 = $var2Tmp;
$var4 = $var1Tmp;

Il existe un moyen de le faire en 1 seule ligne :

<?php
[$var1, $var2, $var3, $var4] = [$var4, $var3, $var2, $var1];

L’opérateur de décomposition « … »

Dans un des exercices, il fallait trouver l’intersection de plusieurs tableaux, c’est-à-dire les éléments communs à plusieurs tableaux. Facile ! PHP propose la fonction array_intersect qui sert justement à ça ! Sauf que cette fonction prend en paramètres les tableaux dans lesquels chercher l’intersection.

C’est-à-dire qu’il faut l’appeler comme ça :

<?php
$intersection = array_intersect($array1, $array2, $array3); // Etc.

Et ce que j’ai de mon côté c’est un (gros) tableau qui contient tous mes tableaux à tester. Et selon le jeu de données, je ne sais pas combien j’en ai, donc impossible d’écrire :

<?php
$intersection = array_intersect($bigArray[0], $bigArray[1], $bigArray[2]); // Etc.

C’est là que l’opérateur de décomposition « … » entre en jeu, pour décomposer mon gros tableau en l’ensemble de ces éléments. Et je n’ai plus qu’à faire :

<?php
$intersection = array_intersect(...$bigArray);

La documentation officielle, avec notamment l’utilisation de cet opérateur dans les paramètres.


Bloquez et cassez vos while

Plusieurs fois il a fallu utiliser des while durant Advent of Code. Et alors que je n’étais pas un grand fan, j’utilise de plus en plus les while(true). Mais ça reste « dangereux » à coder car tant que le code n’est pas finalisé, chaque test peut entraîner une boucle infinie… Il est donc intéressant d’utiliser un « bloqueur » :

<?php
$block = 0;
while (true) {

	// Je manipule des données
	// Je travaille ma condition d'arrêt

	// Code de sécurité ---------
	$block++;
	if ($block > 1000) {
		break;
	}
	// --------------------------
}

Ici, je pense qu’il n’y aura pas + de 1.000 itérations possibles, donc si j’atteins les 1000, je « casse » mon while et j’évite ma boucle infinie.

De la même manière, avec un while true, il faudra utiliser « break » pour sortir de la boucle, une fois la condition d’arrêt atteinte. La condition d’arrêt est donc déplacée directement dans la logique que dans les parenthèses du while. À l’usage, je trouve ça plus lisible.

Une fois que le code de mon while est finalisé, je peux bien sûr retirer ce bloqueur.


Les fonctions liées aux tableaux

PHP propose de nombreuses fonctions disponibles pour manipuler des tableaux. En voici quelques-unes que j’ai utilisées régulièrement durant Advent of Code :

  • array_unique : Ne garder qu’une seule occurrence de chaque élément du tableau (tri des doublons)
  • str_split : Transformer une chaîne de caractères en tableau
  • array_intersect : L’intersection des éléments de plusieurs tableaux (les éléments communs)
  • array_slice : Découper un tableau et n’en garder qu’une portion
  • array_search : Retrouver la clé d’un élément précis dans un tableau
  • array_count_values : Compter les occurrences de chaque élément
  • array_merge : Fusionner des tableaux
  • XXsort : Trier un tableau, les X sont à remplacer selon le type de tri souhaité

C’est compliqué de les connaître toutes… Mais c’est une bonne pratique, quand on manipule un tableau en PHP, de se demander s’il n’existe pas déjà une fonction qui peut réaliser ce qu’on cherche à faire.

Voici la liste des fonctions natives à PHP pour manipuler des tableaux.


Aller + loin dans la manipulation des tableaux avec les fonctions de callback et les Collections

En complément du point précédent, pour manipuler des tableaux et leurs éléments en PHP, 2 autres concepts sont intéressants. Je ne les maîtrise pas (encore) suffisamment pour rentrer trop dans les détails.

Les fonctions array_map, array_filter ou array_reduce permettent de traiter des tableaux en y appliquant des fonctions de callback. On peut donc se passer des for ou des foreach tout en traitant tous les éléments du tableau. Exemple :

<?php
$words = ['bryan', 'is', 'in', 'the', 'kitchen'];
$words = array_map('strtoupper', $words);

// $words contient alors ['BRYAN', 'IS', 'IN', 'THE', 'KITCHEN'];

Les Collections sont des librairies tierces, par exemple très populaires dans Laravel, qui vont permettre de manipuler les tableaux dans une logique objet pour avoir quelque chose de plus lisible et agréable que les fonctions ci-dessus :

On retrouve des choses similaires dans les autres frameworks PHP également.


Et voilà, ça fait 10 astuces ou bonnes pratiques ! J’espère que vous aurez appris des choses !

Personnellement, je suis toujours attiré par ces challenges de programmation, c’est une bonne occasion de se frotter à des complexités algorithmiques, et donc de creuser le langage + en profondeur, d’échanger avec d’autres développeurs sur les solutions trouvées, de découvrir des pratiques, des astuces, etc. Ça permet vraiment de progresser en tant que développeur.

2021 commence tout juste, je vous souhaite à toutes et à tous une très bonne année, riche en code, et en bonnes pratiques de programmation !

Pour réagir à cet article, n’hésitez pas à m’envoyer un message, ou à me solliciter sur Twitter !

Et rendez-vous en fin d’année pour Advent of Code 2021 !

Et encore merci à Jimmy qui, sans le vouloir, m’a motivé à poursuivre le challenge d’Advent of Code, jusqu’au jour 19 (après c’est devenu compliqué avec les vacances et les enfants) ! N’hésitez pas à le suivre sur Twitter ou sur Youtube pour du live coding PHP.


Si vous appréciez les challenges de programmation, je vous invite à découvrir la plateforme créée par Web and Cow :

WAC Code Challenge
Pour progresser, t’entraîner et devenir un super dev !

Publié par Arthur Weill

Directeur de l’agence rennaise Web and Cow, directeur digital du Groupe Valorex, impliqué dans l'informatique et la programmation depuis plus de 10 ans.