Article un peu spécial, je pars d’un problème tout simple et je vous propose 10 façons différentes de le coder.

Le problème, c’est celui du premier challenge de https://code-challenge.webandcow.com/, le Pierre/Feuille/Ciseaux.

En entrée, une chaîne de caractères, du genre « PFC », qui représente les coups de mon adversaire. Comme je suis trop fort, je le bats à chaque fois et je dois donc retourner la chaîne de caractères : « FCP ».

Pour rappel :

  • Pierre bat Ciseaux
  • Ciseaux bat Feuille
  • Feuille bat Pierre

Version 1 : données d’entrée et de sortie, un for, du if/else et plein de variables

// 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.

Version 2 : j’enlève des variables et je rajoute un traitement d’erreur

$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.

Version 3 : je retire les else grâce au continue

$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 !

Version 4 : je retire les if grâce au switch

$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.

Version 5 : je passe du for au foreach avec str_split

$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.

Version 6 : je crée des constantes pour améliorer la lisibilité

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.

Version 7 : je crée un tableau de correspondances

$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.

Version 8 : je crée une fonction

$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.

Version 9 : je me passe du foreach grâce à array_map

$plays = str_split($input);
$counterPlays = array_map('counterPlay', $plays);
$response = implode($counterPlays);

// ou en 1 ligne
$response = implode(array_map('counterPlay', str_split($input)));

Si 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.

Version 10 : avec array_reduce et une constante tableau

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.

Conclusion

Pour aller encore + loin, j’aurais pu faire une version objet, et pourquoi pas aussi faire les tests unitaires de mes fonctions. Je garde ça pour le corrigé d’un challenge + complexe 😉

En tout cas, gardez en tête que pour un problème donné il peut y avoir différentes solutions, faisant appel à des notions + ou – avancées du langage. Le + important, c’est de respecter les conventions, les bonnes pratiques, de garder un code lisible, pour vous, vos (éventuels) collègues, et le vous du futur. Pour éviter de revenir sur votre code 6 mois + tard et de dire : « Mais qu’est-ce que c’est que cette ##### ??!! »

Par contre, au bout de 6 mois, vos compétences et connaissances du langage se seront développées et vous ferez donc naturellement évoluer votre code vers quelque chose + avancé.

Pour d’autres bonnes pratiques en PHP :

PS : si jamais vous n’avez pas la référence sur les erreurs => https://www.youtube.com/watch?v=iSHPVCBsnLw&t=38


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.