T: / Corrigés des challenges / PHP
On commence par se passer des if/else et on termine avec des fonctions de callback.
Lorsque l’on aborde un langage de programmation, c’est fascinant comme une même problématique peut être codée de plein de façons différentes. Même ici, résoudre le challenge « Pierre – Feuille – Ciseaux » peut se faire de (au moins) 11 façons différentes !
Avec cet article, on va voir ensemble plusieurs façons de coder une même problématique, en utilisant des outils du langage de plus en plus avancés. Cela te permettra de découvrir des techniques pour mieux structurer ton code.
Au programme :
// Données d'entrée
$input = 'PFCFFPC';
// Résultat attendu
$result = 'FCPCCFP';
$response = '';
$length = strlen($input);
for ($i = 0; $i < $length; $i++) {
$play = $input[$i];
if ($play === 'P') {
$counterPlay = 'F';
} elseif ($play === 'F') {
$counterPlay = 'C';
} else {
$counterPlay = 'P';
}
$response .= $counterPlay;
}
// Test de la solution trouvée
if ($response === $result) {
echo 'Challenge OK';
}
Il est important ici de bien effectuer le strlen avant le for. Sinon le strlen va s’effectuer à chaque itération.
Pour les prochains exemples, je ne reprendrais pas à chaque fois la déclaration de $input, $result, ni le test final.
$response = '';
$length = strlen($input);
for ($i = 0; $i < $length; $i++) {
if ($input[$i] === 'P') {
$response .= 'F';
} elseif ($input[$i] === 'F') {
$response .= 'C';
} elseif ($input[$i] === 'C') {
$response .= 'P';
} else {
// Erreur... encore un qui veut jouer Puit !
}
}
Je teste donc bien précisément le cas du « C » et je garde un dernier else pour une éventuelle erreur.
Je fais l’économie de mes variables $play et $counterPlay.
$response = '';
$length = strlen($input);
for ($i = 0; $i < $length; $i++) {
if ($input[$i] === 'P') {
$response .= 'F';
continue;
}
if ($input[$i] === 'F') {
$response .= 'C';
continue;
}
if ($input[$i] === 'C') {
$response .= 'P';
continue;
}
// Erreur... non, Puit ça n'existe pas !
}
Comme le continue va directement passer à l’itération suivante, plus besoin de else !
$response = '';
$length = strlen($input);
for ($i = 0; $i < $length; $i++) {
switch ($input[$i]) {
case 'P':
$response .= 'F';
break;
case 'F':
$response .= 'C';
break;
case 'C':
$response .= 'P';
break;
default:
// Erreur... il triche le coquin
break;
}
}
Le switch se prête très bien à ce genre de situation.
Pour aller + loin, tu peux consulter l’article dédié à se passer des else et if en PHP.
$response = '';
foreach (str_split($input) as $play) {
switch ($play) {
case 'P':
$response .= 'F';
break;
case 'F':
$response .= 'C';
break;
case 'C':
$response .= 'P';
break;
default:
// Erreur... Euh... Lézard ? Sérieux ?
break;
}
}
Le str_split peut être mis directement dans le foreach, pas nécessaire de créer une variable avant.
define('PIERRE', 'P');
define('FEUILLE', 'F');
define('CISEAUX', 'C');
$response = '';
foreach (str_split($input) as $play) {
switch ($play) {
case PIERRE:
$response .= FEUILLE;
break;
case FEUILLE:
$response .= CISEAUX;
break;
case CISEAUX:
$response .= PIERRE;
break;
default:
// Erreur... Euh... Le Lézard mange la Feuille ?
break;
}
}
On a un code qui se lit tout seul, et qui nous protège des fautes de frappe dans les chaînes de caractères.
$response = '';
$match = [
PIERRE => FEUILLE,
FEUILLE => CISEAUX,
CISEAUX => PIERRE
];
foreach (str_split($input) as $play) {
$response .= $match[$play];
}
D’un coup on économise quelques lignes ! Plus besoin de switch non plus.
$response = '';
foreach (str_split($input) as $play) {
$response .= counterPlay($play);
}
function counterPlay(string $play): string
{
$match = [
PIERRE => FEUILLE,
FEUILLE => CISEAUX,
CISEAUX => PIERRE
];
if (isset($match[$play])) {
return $match[$play];
}
// Erreur... Quoi ? Spock ? Qu'est ce qu'il vient faire là ?
}
Ma fonction est typée.
J’ai rajouté le test d’existence de la correspondance par rapport à la version précédente.
L’instruction match est disponible à partir de PHP 8.
function counterPlay(string $play): string
{
return match($play) {
PIERRE => FEUILLE,
FEUILLE => CISEAUX,
CISEAUX => PIERRE,
default => ''
};
}
Toutes les infos sur match, qui permet de faire des choses bien + complexes et puissantes que l’exemple ci-dessus.
$plays = str_split($input);
$counterPlays = array_map('counterPlay', $plays);
$response = implode($counterPlays);
// ou en 1 ligne
$response = implode(array_map('counterPlay', str_split($input)));
Lorsque la fonction implode est appelée avec un seul paramètre, qui est un tableau, alors la « glue » est « » (chaîne de caractère vide).
Si vous trouvez la version en 1 ligne complexe à lire, préférez une version en 2 ou 3 lignes, avec 1 ou 2 variables intermédiaires.
Si tu n’es pas familier de array_map, n’hésite pas à découvrir d’autres exemples d’utilisation de array_map en PHP.
define('MATCH', [
PIERRE => FEUILLE,
FEUILLE => CISEAUX,
CISEAUX => PIERRE
]);
$response = array_reduce(
str_split($input),
'counterPlayReduce',
'' // Permet de définir $counterPlays à '' initialement
);
function counterPlayReduce(string $counterPlays, string $play): string
{
if (isset(MATCH[$play])) {
return $counterPlays . MATCH[$play];
}
// Erreur... Donc Spock mange le Lézard ?
}
Il est possible de mettre un tableau dans une constante.
Il faut que je définisse « initial » de array_reduce à « » sinon il prend null et ne respecte pas le typage de ma fonction.
Chaque technique présentée est voulue ici comme une opportunité d’affiner ta compréhension de PHP. C’est en diversifiant tes approches d’une problématique donnée que tu trouveras des méthodes pour optimiser ton code.
Continue de tester, d’explorer, d’expérimenter pour monter en compétences… Et tu peux le faire tout de suite avec un de nos challenges de code 😉
Pour aller + loin, ce challenge Pierre Feuille Ciseaux a aussi été l’occasion d’une introduction aux tests unitaires avec PHPUnit.
Other content to discover