Page 1 sur 1

Boucle récursive

Posté : 09 sept. 2008, 20:00
par xaccrocheur
Bonjour ; J'ai un PB avec une fonction récursive, qui a tendance à s'exécuter une fois de trop
function recursive_readdir ($dir) {
$goodList = array();
$tempList = array();

	$dir = rtrim ($dir, '/'); // on vire un eventuel slash 
	if (is_dir ($dir)) // si c'est un repertoire
		$dh = opendir ($dir);
	else {
		echo $dir, ' n\'est pas un repertoire valide'; // sinon on sort! Appel de fonction non valide
	exit;
	}

	while (($file = readdir ($dh)) !== false ) {
		if ($file !== '.' && $file !== '..' && $file !== 'img' && $file !== 'tmp') {
		$path =$dir.'/'.$file;
			if (is_dir ($path)) { //si on tombe sur un sous-repertoire
					recursive_readdir ($path); // appel recursif 
					}
			else // si il s'agit d'un fichier
				$pos = strrpos($file, '.');
		    if($pos===false) 
		    		{ continue;	
		    		} else {
		        $good = substr($file, $pos+1);
		    		}
		    		// On cherche si l'extention est "mp3"
		    if ($good == 'mp3') {
						// echo '<p>Ya des mp3 dans ', $dir, ' !</p>';
						$tempList[] = $dir; // Construction de la liste
						}
		}
	}
	closedir ($dh);
$goodList = array_unique($tempList);
	Print_R($goodList);
//	return $goodList;
}
recursive_readdir('.');
Produit la sortie suivante :
Array ( [0] => ./elephant/elephant_in_paris ) Array ( ) Array ( [0] => ./azero/demos ) Array ( [0] => ./azero/counternatures ) Array ( ) Array ( [0] => ./les_intouchables/now ) Array ( [0] => ./les_intouchables/demos_enfer ) Array ( [0] => ./les_intouchables/touche ) Array ( [0] => ./les_intouchables/tamuziga ) Array ( ) Array ( )
... Deux fois. Observations :

- Ça fait le boulot que je lui demande : Lister les répertoires contenant des fichiers mp3.
- Ça fait ce boulot une fois par niveau d'arborescence. Bon, comment n'obtenir qu'un sortie, la dernière, une fois qu'on a bien tout récursé ?)
- Print_R me sort ça [0] => ./elephant/elephant_in_paris, toujours [0] Donc en fait, il s'agit d'un tableau de tableaux contenant chacun juste une référence (0, pas 1 ? Bon) Mais j'arrive PAS à piger où se crée cette itération en trop :cry:
- J'ai bien compris qu'à chaque récursion, les valeurs étaient ré-initialisées, d'où l'utilité de les stocker dans un tableau... Mais comment utiliser le "return" correctement ? Au final, je voudrais "juste" récupérer un tableau que je pourrai "cycler" comment ça :

Répertoire précédent : $goodList[-1]
Répertoire courant : $goodList[1]
Répertoire suivant : $goodList[+1]

J'ai donc besoin d'un tableau sensiblement indentique à ce que j'ai, mais un peu différent, puisque là les clefs d'index sont les mêmes à chaque fois... Help ?

Merci de votre patience

Posté : 09 sept. 2008, 22:47
par sadeq
Voici une correction pour comparer :
<pre>
<?php
// Fonction d'exploration
function recursive_readdir ($dir) {
     $list = array();
	// sortir si faux dossier
	if (!is_dir ($dir)) { echo $dir .' n\'est pas un repertoire valide'; return; }
 	// ouvrir le dossier
	$dh = opendir ($dir);
 	// et parcourir son contenu
    while (($file = readdir ($dh)) !== false ) {
      if ($file != '.' && $file != '..' && $file != 'img' && $file != 'tmp') {
	  	//si on tombe sur un sous-repertoire
      	if (is_dir ($dir.'/'.$file)) {
          	$list = array_merge($list, recursive_readdir ($dir.'/'.$file)); // appel recursif + récup list
      	}
    	else {
                // On cherche si l'extention est "mp3"
				$infos = pathinfo($dir.'/'.$file);
          		if ($infos['extension'] == 'mp3') {
					// echo '<p>Ya des mp3 dans ', $dir, ' !</p>';
					$list[] = $dir.'/'.$file; // Construction de la liste
                }
        }
	  }
   }
   closedir ($dh);
   return $list;
}
// Programme principal
$list = recursive_readdir('c:/wamp/www');
print_r($list);
?>
</pre>
Le plus important à comprendre est que la récursivité réinitialise la variable $list car elle est locale, la solution utilisée dans le script est array_merge() qui cumule le résultat de la récursivité à chaque cycle dans la variable empilée $list. A la fin de la récursivité $list se retrouve avec le cumul de toutes les autres variable $list empilée par récursion. Le dernier état étant retourné au programme principal qui a initié l'appel.

D'autres solutions existent pour que toutes les instances d'une fonction récursive stockent leurs données produites dans un même conteneur récupérable au niveau haut (programme principal)
On peut citer l'usage d'une variable globale ou l'usage d'un passage de paramètre par adresse à la fonction récursive.

Posté : 11 sept. 2008, 04:34
par xaccrocheur
Voici une correction pour comparer :
Wow, Sieur Sadeq, vous m'avez steacké sur ce coup. Prendre le temps de lire mon code tout naze, puis le ré-écrire en corrigeant également les erreurs de style, puis tester tout ça sur votre serveur, non vraiment, je suis impressionné par tant de diligence, en anglais on dit "you are a gentleman and a scholar" :)

Me voilà avec une boucle récursive certes infiniment + élégante, mais qui me retourne toujours autant, sinon plus d'éléments : OK, les réps/sous reps se retrouvent dans $list, (par ailleurs parfaitement construite, comme je la souhaitais, merci) mais autant de fois que la boucle a trouvé de mp3s, ce qui est d'ailleurs son comportement normal.
Mais je cherche un moyen de réduire cette liste en ne gardant qu'une seule occurrence à chaque fois.
array_unique() n'est pas une option, puisqu'elle conserve les index sans les réorganiser, et la liste est inexploitable si on doit naviguer dedans (<= et =>).

En somme, mon problème, de balèze, est devenu tout petit : Quelqu'un sait-il comment réduire un array() à *une* seule occurrence à chaque fois ?

Merci encore pour votre aide

Mon code actuel :
function recursive_readdir ($myDir) {
     $list = array();
    // sortir si faux dossier
    if (!is_dir ($myDir)) { echo $myDir .' n\'est pas un repertoire valide'; return; }
     // ouvrir le dossier
    $dh = opendir ($myDir);
     // et parcourir son contenu
    while (($file = readdir ($dh)) !== false ) {
      if ($file != '.' && $file != '..' && $file != 'img' && $file != 'tmp') {
          //si on tombe sur un sous-repertoire
          if (is_dir ($myDir.'/'.$file)) {
              $list = array_merge($list, recursive_readdir ($myDir.'/'.$file)); // appel recursif + récup list
          }
        else {
                // On cherche si l'extention est "mp3"
                $infos = pathinfo($myDir.'/'.$file);
                  if ($infos['extension'] == 'mp3') {
                    // echo '<p>Ya des mp3 dans ', $myDir, ' !</p>';
                    $list[] = $myDir; // Construction de la liste
                }
        }
      }
   }
   closedir ($dh);
   return $list;
}
// Programme principal
$list = recursive_readdir('.');
var_dump($list);
Et sa sortie :
array(37) { [0]=> string(28) "./elephant/elephant_in_paris" [1]=> string(28) "./elephant/elephant_in_paris" [2]=> string(28) "./elephant/elephant_in_paris" [3]=> string(28) "./elephant/elephant_in_paris" [4]=> string(28) "./elephant/elephant_in_paris" [5]=> string(28) "./elephant/elephant_in_paris" [6]=> string(28) "./elephant/elephant_in_paris" [7]=> string(28) "./elephant/elephant_in_paris" [8]=> string(13) "./azero/demos" [9]=> string(13) "./azero/demos" [10]=> string(22) "./azero/counternatures" [11]=> string(22) "./azero/counternatures" [12]=> string(22) "./azero/counternatures" [13]=> string(22) "./les_intouchables/now" [14]=> string(22) "./les_intouchables/now" [15]=> string(22) "./les_intouchables/now" [16]=> string(22) "./les_intouchables/now" [17]=> string(30) "./les_intouchables/demos_enfer" [18]=> string(30) "./les_intouchables/demos_enfer" [19]=> string(30) "./les_intouchables/demos_enfer" [20]=> string(30) "./les_intouchables/demos_enfer" [21]=> string(25) "./les_intouchables/touche" [22]=> string(25) "./les_intouchables/touche" [23]=> string(25) "./les_intouchables/touche" [24]=> string(25) "./les_intouchables/touche" [25]=> string(27) "./les_intouchables/tamuziga" [26]=> string(27) "./les_intouchables/tamuziga" [27]=> string(27) "./les_intouchables/tamuziga" [28]=> string(27) "./les_intouchables/tamuziga" [29]=> string(27) "./les_intouchables/tamuziga" [30]=> string(27) "./les_intouchables/tamuziga" [31]=> string(27) "./les_intouchables/tamuziga" [32]=> string(27) "./les_intouchables/tamuziga" [33]=> string(27) "./les_intouchables/tamuziga" [34]=> string(27) "./les_intouchables/tamuziga" [35]=> string(27) "./les_intouchables/tamuziga" [36]=> string(27) "./les_intouchables/tamuziga" }
EDIT bon, j'ai réglé le PB en utilisant quand même array_unique, puis en faisant une nouvelle liste :
function recursive_readdir ($myDir) {
     $list = array();
    if (!is_dir ($myDir)) { echo $myDir .' n\'est pas un repertoire valide'; return; }
    $dh = opendir ($myDir);
    while (($file = readdir ($dh)) !== false ) {
      if ($file != '.' && $file != '..' && $file != 'img' && $file != 'tmp') {
          if (is_dir ($myDir.'/'.$file)) {	//si on tombe sur un sous-repertoire
              $list = array_merge($list, recursive_readdir ($myDir.'/'.$file)); // appel recursif + récup list
          }
        else {
                $infos = pathinfo($myDir.'/'.$file);	// On cherche si l'extention est "mp3"
                  if ($infos['extension'] == 'mp3') {
                    $list[] = $myDir; // Construction de la liste
                }
        }
      }
   }
   closedir ($dh);
   return $list;
}
// Programme principal
$list = recursive_readdir('.');
$list = array_unique($list);
//$list = array_flip($list);

foreach ( $list as $dir ) //on parcours le tableau 
{
	$newlist[] = $dir; // Construction de la liste
} 

print_r($newlist);
Sortie :
Array ( [0] => ./elephant/elephant_in_paris [1] => ./azero/demos [2] => ./azero/counternatures [3] => ./les_intouchables/now [4] => ./les_intouchables/demos_enfer [5] => ./les_intouchables/touche [6] => ./les_intouchables/tamuziga )
Hoooorray !! \:D/

Posté : 11 sept. 2008, 09:14
par sadeq
Bonjour, content que ça t'as intéressé.

Pour reconstruire les index du tableau tu peux utiliser : array_values()
$list = array_values($list);

Posté : 13 sept. 2008, 20:29
par xaccrocheur
Salut tout le monde, voici mon code final :
function mp3_dir($myDir)
{
	$subdirlist = array();
	$res = array();
	$dh = opendir($myDir);
	while (($myFile = readdir($dh)) !== false ) {
		if ($myFile != '.' && $myFile != '..' && $myFile != 'img' && $myFile != 'tmp') {
			$infos = pathinfo($myDir.'/'.$myFile);
			if ($infos['extension'] == 'mp3') {
				$contains_mp3 = true;
			}
			if (is_dir($myDir.'/'.$myFile)) {
				$subdirlist[] = $myDir.'/'.$myFile;
			}
		}
	}
	
closedir ($dh);
	
	if($contains_mp3) {
	  $res[] = $myDir.'/'.$myFile;
	}

	foreach($subdirlist as $subdir) {
     $res = array_merge($res, mp3_dir($subdir));
	}
	return $res;
}

print_r(mp3_dir('.'));
Et la sortie :
Array ( [0] => ./elephant/elephant_in_paris/ [1] => ./azero/demos/ [2] => ./azero/counternatures/ [3] => ./les_intouchables/now/ [4] => ./les_intouchables/demos_enfer/ [5] => ./les_intouchables/touche/ [6] => ./les_intouchables/tamuziga/ )
J'ai réussi, en m'arretant pour réfléchir un peu, à éviter de stocker *toutes* les occurences des ssreps trouvés, pour ne stocker *que* la liste, ce qui évite des récursions inutiles.

Au final, je dirai que les récursions, c'est pas si trivial :wink: en tout cas merci encore

pX[/code]