Exemple de recursivitate php. Recursie în PHP - algoritm, aplicație. Apel de funcție recursiv

Astăzi vă voi spune cum să faceți MySQL creați un arbore ierarhic.

Astfel de arbori sunt utilizați atunci când se construiesc categorii ale unui site dinamic, de exemplu, într-un magazin online sau când se afișează comentarii la o postare.

În general, acestea sunt construite oriunde este posibil. Principalul lucru este să îl construiți și să îl aplicați corect.

Cel mai important lucru la construirea unui arbore ierarhic este structura corectă a bazei de date! De exemplu, luați în considerare structura bazei de date în care sunt stocate categoriile de site. Pentru un exemplu simplu, tabelul va avea 3 câmpuri:

  1. id- cheia categoriei
  2. parent_id— id-ul categoriei părinte
  3. nume– titlul secțiunii

Să creăm un tabel executând o interogare SQL în PHPMyAdmin:

CREATE TABLE `categories` (`id` INT NOT NULL AUTO_INCREMENT , `parent_id` INT NOT NULL , `name` VARCHAR(50) NOT NULL , PRIMARY KEY (`id`));

Acum trebuie să ne umplem tabelul cu înregistrări. Ca rezultat, ar trebui să obțineți un tabel cam așa:

Puteți completa un tabel de testare cu următoarea interogare:

INSERT INTO `categories` (`id`, `parent_id`, `name`) VALORI (1, 0, "Section 1"), (2, 0, "Section 2"), (3, 0, "Section 3" ), (4, 1, „Secțiunea 1.1”), (5, 1, „Secțiunea 1.2”), (6, 4, „Secțiunea 1.1.1”), (7, 2, „Secțiunea 2.1”), (8 , 2, „Secțiunea 2.2”, (9, 3, „Secțiunea 3.1”);

Și acum atenție! Apoi, în mod logic, trebuie să faceți selecții din baza de date într-o buclă pentru a selecta fiecare categorie și subcategoria acesteia. DAR! Este în regulă dacă există mai multe categorii în baza de date, ceea ce, de asemenea, nu este corect în principiu. Dacă site-ul este un magazin online și are o sută de categorii și același număr de subcategorii? Atunci necaz! Un număr necunoscut de interogări la baza de date va duce la o încetinire a site-ului sau o prăbușire completă a serverului mysql.

Puteți utiliza o singură interogare a bazei de date pentru a selecta toate categoriile și subcategoriile acestora.

Să facem o cerere și să creăm o matrice convenabilă pentru lucrări ulterioare.

//Selectează datele din baza de date $result=mysql_query("SELECT * FROM categories"); //Dacă există înregistrări în baza de date, formăm un tablou dacă (mysql_num_rows($rezultat) > 0)( $cats = array(); //În buclă formăm o matrice de secțiuni, cheia va fi id-ul a categoriei părinte, precum și o serie de secțiuni, cheia va fi categoria id while($cat = mysql_fetch_assoc($result))( $cats_ID[$cat["id"]] = $cat; $cats[$ cat["parent_id"]][$cat["id"]] = $cat;

Selectarea tuturor datelor din tabel categoriiși formează o matrice asociativă $pisici, cheia va fi id-ul categoriilor părinte.

Acum vom construi un copac. Pentru a construi vom folosi funcţie recursivă.

Arborele ierarhic va avea următoarea structură:

  • Secțiunea 1
    • Secțiunea 1.1
      • Secțiunea 1.1.1
    • Secțiunea 1.2
  • Secțiunea 2
    • Secțiunea 1.1
    • Secțiunea 1.2
  • Secțiunea 3
    • Secțiunea 3.1

Să creăm o funcție recursivă build_tree() . Va construi arborele nostru ierarhic de absolut orice cuibărit.

Funcția build_tree($cats,$parent_id,$only_parent = false)( if(is_array($cats) și isset($cats[$parent_id]))( $tree = "

    "; if($only_parent==false)( foreach($cats[$parent_id] as $cat)( $tree .= ""; ) )elseif(is_numeric($only_parent))( $cat = $pisici[$parent_id ][$only_parent] $tree .= ";
  • „.$cat[„nume”]”. #".$cat["id"]; $tree .= build_tree($cats,$cat["id"]); $tree .= "
  • "; ) $tree .="
"; ) altfel returnează nul; returnează $tree; )

Funcția preia o matrice de secțiuni și id-ul secțiunii. În buclă parcurgem subcategorii și dacă au mai multe secțiuni, atunci funcția este lansată din nou cu parametri noi (o nouă matrice de secțiuni și id-ul secțiunii care trebuie construită). Așa se formează un copac de orice cuibărit!

Pentru a construi un arbore, scriem în cod:

Echo build_tree($pisici,0);

Așadar, în doi pași am creat un arbore ierarhic de secțiuni de site și nu contează câte secțiuni sunt!

UPD Dacă aveți nevoie de un arbore de categorii în ordine inversă, cunoscând ID-ul categoriei, atunci trebuie să utilizați funcția:

Funcția find_parent ($tmp, $cur_id)( if($tmp[$cur_id]["parent_id"]!=0)( return find_parent($tmp, $tmp[$cur_id]["parent_id"]); ) return ( int)$tmp[$cur_id]["id"];

Această funcție preia o serie de categorii, a căror cheie este id-ul categoriei și id-ul categoriei de la care să urce.

Pentru a construi un astfel de arbore, rulați funcția build_tree cu următorii parametri:

Echo build_tree($cats,0,find_parent($cats_ID,YOUR_CATEGORY_ID));

Aveți întrebări? Întrebați în comentarii

Mulți dintre voi știți ceva de genul recursiunii. Dar, pentru orice eventualitate, recursiunea este chemarea unei funcții în sine. Cu toate acestea, toată lumea cunoaște această definiție, dar ambiguitățile apar odată cu înțelegerea. Am decis în acest articol să analizez recursiunea în PHP folosind un exemplu destul de simplu și real din practică.

Nu este un secret pentru nimeni că toate datele (cu rare excepții) care provin din formulare trebuie să fie trecute prin funcție htmlspecialchars() pentru a evita XSS. Și cred că știți că unele date de intrare sunt matrice (de exemplu, la încărcarea fișierelor). Și sarcina noastră este să scriem o funcție care acceptă o matrice a tuturor datelor primite și, de asemenea, să luăm în considerare faptul că printre aceste date există și matrice, în interiorul cărora pot exista și matrice și așa mai departe.

Iată codul funcției:

funcția xss($date) (
if (is_array($date)) ( // Dacă aceasta este o matrice
$rezultat = matrice(); // Creați o nouă matrice
foreach ($date ca $cheie => $valoare) ( ​​// Circulați prin matricea originală
$rezultat[$cheie] = xss($valoare); // Apelați recursiv funcția xss
}
returnează $rezultat; // A returnat tabloul „protejat”.
}
returnează htmlspecialchars($date, ENT_QUOTES); // Dacă aceasta nu este o matrice, atunci apelați htmlspecialchars()
}
$date = xss($_CERERE); // Exemplu de apel de funcție
?>

Punctul cheie aici este că matricele interne din interiorul primit $date pot fi multe, deci fără recursiunea în PHP nu pot trece pe aici. Și algoritmul în sine este simplu:

  1. Dacă datele primite NU sunt o matrice, atunci pur și simplu apelați funcția htmlspecialchars().
  2. Dacă datele primite sunt o matrice, atunci repetăm ​​toate elementele matricei și apelăm aceeași funcție pentru fiecare. Și apoi revenim la subiect 1 .

Înțeleg asta pentru începători recursiunea în PHP- acesta este un lucru destul de complicat, dar recomand să parcurgeți direct codul ca și cum ați fi un interpret PHP. Atunci totul va deveni mai clar.

  • Algoritmi
  • Scrierea acestui articol a fost determinată de ore de gândire și experimentare în domeniul construirii listelor ierarhice. Inițial, logica a fost testată pe interogări SQL, dar ulterior am decis să o implementez în PHP pentru a elimina dependența de SGBD. Folosind un exemplu simplu, voi arăta cum puteți merge de la rădăcina ierarhiei la fiecare element final și înapoi, informațiile sunt mai probabile pentru începători.

    Deci, ierarhia de testare cu care trebuie să lucrăm:

    Baza de date are cel mai simplu tabel de pe cel mai simplu server MSSQL, vom sări peste subtilitățile conexiunii, scopul nostru este să înțelegem ierarhia și recursiunea.

    Să creăm un tabel:

    CREATE TABLE .( IDENTITY(1,1) NOT NULL, -- câmp unic, NULL cu incrementare automată, -- acest câmp indică un element la un nivel superior, conține uid-ul părintelui (255) NULL, (50) NULL, -- drepturi de acces) ON
    Să completăm informațiile:

    Descrierile câmpurilor sunt în comentarii, puțin mai multe despre domeniu acces:

    În mod implicit, în sistemul meu, pentru fiecare document nou, moşteni, adică moștenirea de la părinte. Pentru experimentul nostru, vom scrie grupuri de domenii pentru unele elemente. Într-un grup Utilizatori de domeniu contul meu este disponibil, dar în Secretul grupului AD Nu sunt aici.

    Ce mai avem. O matrice care conține o listă a grupurilor mele de domenii. Se obține destul de simplu, autentificarea Windows este activată pe IIS, totul funcționează transparent, în PHP login-ul utilizatorului este în variabila $_SERVER[“AUTH_USER”], apoi folosind o cerere LDAP obținem o listă de grupuri.

    Acum îmi propun să obținem datele necesare și să ajungem direct la obiect:

    $stmt = $PDO->query("SELECT * FROM Test"); $tabel = $stmt->fetchAll(); //Obține tabelul din baza de date $groups = LDAP::getGroups("$login"); //Obțineți grupuri ActiveDirectory

    Sarcina nr. 1

    Trebuie să înveți să lucrezi cu ierarhia ca un arbore și nu o listă. Nivelul de cuibărit nu este cunoscut în prealabil și poate fi oricare, prin urmare trebuie să existe un instrument universal care să vă permită să traversați copacul atât de sus în jos, cât și în direcția opusă.

    Sarcina nr. 2

    Este necesar să gestionați în mod flexibil accesul, adică să acordați drepturi grupurilor, documentelor individuale etc., prin analogie cu sistemul de fișiere NTFS, puteți închide drepturile pentru un întreg folder, dar pentru un document din acest folder puteți tăia acces - același lucru ar trebui să se întâmple și cu noi.

    Sarcina nr. 3

    Este necesar să ascundeți de utilizatori resurse la care nu au acces, dar cel mai important, dacă aveți drepturi la cel puțin un document undeva adânc într-o ramură care este închisă acestuia, faceți vizibile elementele care conduc la acest document (în caz contrar cum va ajunge utilizatorul la el?)

    Iată funcția de bază reală:

    $array = array(); //Ieșire funcția matrice recursivă($date, $pid = 0, $level = 0)( global $array; foreach ($date ca $rând) ( //iterează rândurile dacă ($row["pid"] == $ pid) ( //Începe cu liniile al căror pid este transmis funcției, pentru noi este 0, adică rădăcina site-ului //Colectează linia într-un tablou asociativ $_row["uid"] = $row[ "uid"]; $ _row["pid"] = $row["pid"]; $_row["nume"] = $_row["nume"] = str_pad("", $level*3, "." ).$row[" name"] //Utilizați funcția str_pad pentru a adăuga puncte $_row["level"] = $level //Adăugați un nivel $array = $_row; //Linia a fost procesată, acum să rulăm aceeași funcție pentru uid-ul curent, adică // rândul copil va fi inversat (pentru care acest uid este pid-ul) recursive($date, $row["uid"], $nivel + 1) ) ) recursiv($tabel); //Lansa
    Descrierea a fost dată în mare parte în comentarii, dar pentru a spune simplu - după ce bucla foreach trece prin linie și face ceva cu datele (în cazul nostru, pur și simplu copiază datele într-o altă matrice, adăugând câmpul de nivel și punctele la numele), rulează aceeași funcție, trecându-i uid-ul șirului și, deoarece în condiția if îl comparăm cu pid, atunci următoarea rulare va prelua cu siguranță elementele copil. Bucla foreach iterează prin toate rândurile al căror uid părinte se potrivește cu valoarea transmisă, astfel încât, repornindu-se, funcția va funcționa pe fiecare element al fiecărui nivel. Pentru claritate, trecem de nivel și prin creșterea acestuia cu unu. Ca rezultat, vom vedea ce document are ce nivel de imbricare.

    Ieșirea matricei $array la browser:

    Nu mai e rău, nu?

    Acum să ne complicăm puțin funcția:

    $array = array(); //matrice de ieșire $array_idx_lvl = array(); //indexare după funcție la nivel de câmp recursiv($date, $pid = 0, $level = 0, $cale = "", $access_parent = "moștenire")( global $array; global $array_idx_lvl; //Index după nivel global $groups //domain groups //iterează liniile pentru fiecare ($date ca $row) ( //Începe cu liniile al căror pid este transmis funcției, pentru noi acesta este 0, adică rădăcina site-ului dacă ($); row["pid "] == $pid) ( //Colectați șirul într-o matrice asociativă $_row["uid"] = $row["uid"]; $_row["pid"] = $row["pid "]; $_row[ "nume"] = str_pad("", $level*3, ".").$row["nume"]; $_row["level"] = $level; //Adăugați nivelul $ _row["path"] = $cale."/".$row["nume"] //Adăugați un nume la cale $_row["view"] = "" //Rezolvați accesele if($row[); "access"] == "moștenire ") ( $_row["access"] = $access_parent; //Dacă există moștenire, fă-o ca părintele) altfel ( $_row["access"] = (in_array($row) ["access"], $groups)) ? "allow" : "deny" ) $array[$row["uid"]] //Matrice rezultată indexată de uid //Pentru o selecție rapidă; creați un index $array_idx_lvl[$level][$ row["uid"]] = $row["uid"];
    //Rândul a fost procesat, acum să rulăm aceeași funcție pentru uid-ul curent, adică //rândul copil (pentru care acest uid este pid-ul) va fi inversat) recursive($date, $row["uid) "], $level + 1, $_row["cale"], $_row["acces"]);

    ) ) ) recursiv($tabel); //Lansa Să ne uităm la el în ordine: 1. Câmp adăugat

    2. Matricea rezultată este acum formată nu în ordine, începând de la zero, ci cu referire la uid - $array[$row["uid"]] = $_row;. În acest caz, acest lucru nu afectează în niciun fel funcționarea scriptului, dar vom avea nevoie de capacitatea de a accesa un rând prin index, și nu prin forță brută într-o buclă, în viitor, când vom analiza trecerea prin copac în direcția opusă.

    3. Index adăugat $array_idx_lvl = array();. De asemenea, vom avea nevoie de acest index mai târziu, semnificația este aceasta - setul de rezultate nu este adăugat la un heap, ci împărțit în matrice indexate pe nivel.

    4. Câmp Acces. Când o funcție rulează singură, împreună cu alți parametri, își trece setările de permisiuni $_row[„acces”] fiice, apoi se întâmplă următoarele: drepturile sunt verificate - dacă moștenirea este stabilită, atunci drepturile părintelui sunt aplicate, dacă nu, prin in_array Verificăm dacă grupul de domenii specificat în acces se află printre grupurile utilizatorului conectat. Dacă există, adăugați permis la linie, altfel refuzați.

    Rezultatul final:

    Ei bine, am rezolvat coborârea, acum nu mai rămâne decât să ne ocupăm de ascensiune și să completați ultimul câmp vedere, care determină vizibilitatea elementelor. La începutul articolului, am spus de ce este nevoie de acest lucru, dar putem presupune o situație diferită. Să presupunem că decideți să legați o listă arborescentă la meniul de navigare al site-ului, realizată sub forma unei liste derulante pe mai multe niveluri, cu o grămadă de elemente și pur și simplu nu doriți un utilizator care are acces la un singur document să treacă prin toată această matrice și să-și caute articolul într-un meniu voluminos, deoarece, de fapt, trebuie să arate o singură ramură care duce la butonul dorit.

    De ce este nevoie de un pasaj invers aici? Să presupunem că utilizatorului i se refuză accesul la tot conținutul, cu excepția unuia, cel mai îndepărtat document (la ultimul nivel), dacă te gândești bine, ar fi logic să pornești de la ceea ce este disponibil și să-l conduci la rădăcina arborelui, arătând doar elementele necesare.

    Să începem:

    //Funcția urcă arborele cu un nivel de la uid-ul dat, setează //proprietatea de vizibilitate pentru ea însăși și pentru părinte în funcție de acces sau de vizibilitatea setată anterior... function backRecursive($uid, $view = null, $ ident = 0) ( global $array; //Dacă ați urcat nu mai mult de un nivel if($ident<= 1) { //Если видимость уже есть - не меняем текущую строку, иначе //проверяем доступ и то что пришло от дочки if($array[$uid]["view"] != "show") { $array[$uid]["view"] = ($array[$uid]["access"] == "allow" or $view == "show") ? "show" : "hide"; } backRecursive($array[$uid]["pid"], $array[$uid]["view"], $ident+1); } }
    Ceea ce face această funcție este că ia ca parametru uid linia care trebuie acționată, accesează acea linie și verifică vizibilitatea. Dacă câmpul de vizualizare nu este afișat (adică arată), ci altceva, verifică ce este sigur și dacă există permite(accesul este deschis), face elementul vizibil, altfel ascuns( ascunde), apoi se lansează, trecându-i pidși setări de vizibilitate, precum și o variabilă $ident crescut cu 1, blocând astfel auto-pornirile ulterioare. La a doua trecere, conform transmisiei pid este localizat elementul părinte, se efectuează aceeași verificare, cu excepția unui lucru, dacă de la copilul din variabilă $view transferat" spectacol„, atunci indiferent de ce, elementul curent va fi și el atribuit spectacol, adică vizibil.

    După părerea mea, lucrul cu un limitator este cea mai bună opțiune, pentru că imaginați-vă situația, la nivelul 10 avem 100 de documente, pentru a parcurge complet întregul arbore, trebuie să rulăm această funcție pe fiecare element, deoarece dacă la ultimul nivel rulăm funcția de 100 de ori, atunci efectuând auto-porniri, căutarea de 100 de ori va ajunge la rădăcină. Dacă înmulțiți cu 10 niveluri, veți obține deja 1000 de cicluri, ceea ce nu este bine, așa că creșterea trebuie efectuată uniform, nivel cu nivel.

    Următorul cod rulează această funcție:

    Funcția startBack())( global $array_idx_lvl; $levels = array_keys($array_idx_lvl); //obține o matrice de niveluri $maxLevel = max($levels); //Găsiți cel mai profund nivel al arborelui //Buclă prin fiecare nivel începând cu cel mai mare pentru ($i = $maxLevel; $i > 0; $i--) ( $uids = array_keys($array_idx_lvl[$i]); //La nivelul curent parcurgem toate elementele și pentru fiecare initiem procesarea si miscarea la 1 nivel pentru fiecare ($uids ca $uid) ( backRecursive($uid); ) ) )
    Aici a fost nevoie de indicele de nivel. Aici trecem de la nivelul cel mai îndepărtat, intrând pe fiecare, procesând fiecare element din acesta.

    Și iată poza:

    Înainte de lansare, am specificat în mod intenționat un grup de permisiuni pentru elementul „Raport fiscal” pentru a arăta clar că codul funcționa corect. În ciuda faptului că accesul la secțiunea „Extrae contabile” este închis, acesta este vizibil.

    Asta e tot, cred că am finalizat sarcina, baza a fost obținută, algoritmul funcționează și poate fi aplicat într-un sistem real.

    Majoritatea limbajelor de programare acceptă funcții recursive, adică funcții care se numesc singure. Acesta este un instrument foarte puternic care vă permite să creați programe destul de elegante și funcționale, dar este folosit destul de rar - aparent datorită complexității sale pentru programatorii începători și capacității de a provoca erori neplăcute. Prin urmare, în postarea de astăzi vom vorbi despre crearea și utilizarea funcțiilor recursive în PHP.

    Din punct de vedere tehnic, funcțiile recursive nu sunt diferite de funcțiile obișnuite. Singura diferență este că undeva în codul funcției există un apel către funcția în sine. De exemplu, dacă scrii

    Funcție test() (
    diferiți operatori
    test();
    diferiți operatori
    }
    atunci aceasta va fi o funcție recursivă.

    Principala dificultate atunci când lucrați cu funcții recursive este că acestea sunt destul de ușor de buclat - pentru a evita acest lucru, trebuie să fiți foarte atenți la posibilitatea de a părăsi funcția fără un apel recursiv, altfel funcția va începe să se numească „la infinit " și va epuiza rapid resursele computerului.

    Una dintre cele mai frecvente utilizări ale funcțiilor recursive este de a traversa copaci, iar astăzi vom încerca să ne uităm la câteva exemple. Primul lucru care îmi vine în minte este rezultatul unui tablou multidimensional. Desigur, PHP are o funcție print_r(), dar vom încerca să facem rezultatul mai frumos și mai potrivit pentru vizualizare printr-un browser.

    Funcția print_array($ar) (
    static $count;

    $count = (isset($count)) ? +$număr: 0;

    $culori = matrice ("#FFCB72", "#FFB072", "#FFE972", "#F1FF72",
    „#92FF69”, „#6EF6DA”, „#72D9FE”);

    dacă ($număr > număr ($culori)) (
    ecou „Adancime maximă de scufundare atinsă!”;
    $count--;
    reveni;
    }

    dacă (!este_matrice($ar)) (

    ";
    reveni; )

    ecou"

    ";
    ecou" \n";
    dacă (este_matrice($v)) (
    ecou" \n";
    }
    }
    ecou"
    $k$v
    ";
    print_array($v);
    ecou"
    ";
    $count--;
    }

    Această funcție folosește o variabilă statică $count, care va conține „adâncimea de imersare” a funcției - o vom folosi pentru a colora matrice imbricate în diferite culori, în funcție de adâncimea de imersare. Variabilele statice își păstrează valoarea atunci când o funcție iese și este destul de convenabil să le folosești în timpul recursiunii. Desigur, ai putea trece la fel de ușor variabila $count ca parametru, dar este mai convenabil să folosești variabile statice...

    Matricea $culori conține o listă de culori diferite care vor fi folosite pentru colorare. În același timp, îl folosim pentru a limita nivelul maxim de recursivitate - de îndată ce toate culorile sunt epuizate (verificați dacă ($count >= count($colors))), funcția va afișa un mesaj care afirmă că adâncimea maximă a fost atins.

    Pentru orice eventualitate (apropo, acest lucru este întotdeauna recomandat pentru a evita orice consecințe neplăcute), verificăm și dacă matricea este transmisă ca argument - dacă nu este cazul, atunci pur și simplu afișăm un mesaj de eroare și termina functia:

    Dacă (!is_array($ar)) (
    echo „Argumentul trecut nu este o matrice!

    ";
    reveni;
    }

    Apoi „deschidem” tabelul (ecou „

    ";) și începeți să căutați secvențial prin matricea transmisă ca argument. Construcție

    În timp ce(lista($k, $v) = fiecare($ar)) (
    ...
    }

    Este una dintre modalitățile standard de a parcurge o matrice - în fiecare trecere a buclei, variabilelor $k și $v li se atribuie următoarele valori ale indexului (sau, așa cum se mai numește, cheie) și valorile ​a elementului de matrice.

    Odată ce avem o pereche cheie-valoare, o afișăm într-un rând de tabel:

    Ecou"

    \n";
    Rețineți că dacă valoarea acestui element este o matrice, cuvântul „matrice” va fi tipărit. Acum verificăm dacă valoarea este o matrice:
    dacă (este_matrice($v))
    iar dacă da, atunci imprimăm (nu complet!) un alt rând al tabelului, sărind peste index (este deja pe linia anterioară):
    ecou" \n";
    Aceasta încheie procesarea perechii cheie-valoare curentă, iar bucla while trece la următoarea pereche. Și când întregul tablou a fost parcurs, tot ce trebuie să facem este să închidem tabelul:
    ecou"
    $k$v
    ";
    și apelăm funcția noastră de imprimare a matricei, specificând matricea imbricată ca argument:
    print_array($v);
    Apoi (după ce funcția apelată recursiv a terminat de rulat) „închidem” rândul tabelului (rețineți că, deoarece funcția noastră imprimă „tabelul complet” - de la la
    , - nu trebuie să închidem tabelul imbricat - funcția se va ocupa de asta însăși - dar trebuie doar să închidem celula și rândul tabelului curent).
    ecou"
    ";
    și reduceți valoarea adâncimii
    $count--; Din ceea ce a fost scris mai sus, este clar că funcția noastră nu știe nimic despre dacă este apelată recursiv sau nu și nu-i pasă de asta - principalul lucru este că o matrice este transmisă ca argument. De asemenea, atunci când se autoapelează pentru a procesa o matrice imbricată, nu-i pasă că este un apel recursiv - nu este diferit de apelarea unei alte funcții. Iar preocuparea programatorului este să se asigure că apelurile recursive se termină undeva, adică funcția își poate termina munca fără a se apela din nou. Odată ce se întâmplă acest lucru, adâncimea apelurilor imbricate va începe să scadă și, în cele din urmă, funcția va „a ieși la suprafață”.

    Rezultatul unei astfel de funcții va arăta cam așa (în exemplu, adâncimea maximă este limitată la al treilea nivel, pentru claritate, ieșirea variabilei $count este adăugată, iar matricea pentru imprimare este setată la matrice (1). , matrice (1.1, 1.2, 1.3), 2, 3 , matrice (3.1, 3.2, matrice (3.21, 3.22, 3.23, matrice (3.231, 3.232), 3.24)), 4)

    contacheievaloare
    0 0 1
    0 1 Matrice
    0
    contacheievaloare
    1 0 1.1
    1 1 1.2
    1 2 1.3
    0 2 2
    0 3 3
    0 4 Matrice
    0
    0 5 4 Imprimarea unei matrice este rareori necesară „în viața reală”, cu excepția poate pentru testarea scripturilor, dar recursiunea poate fi utilă destul de des. De exemplu, o nevoie mai vitală este să procesăm toate fișierele dintr-un director, inclusiv subdirectoarele. De exemplu, pentru a șterge fișiere este convenabil să folosiți funcția recursivă dd() (prescurtare de la Directory Delete): function dd($file) (
    dacă (fișier_există($fișier)) (
    chmod($file,0777);
    dacă (este_dir($fișier)) (
    $handle = opendir($fisier);
    while($filename = readdir($handle))
    if ($filename != "." && $filename != "..") dd($filename."/".$filename);
    closedir($handle);
    rmdir($fișier);
    ) altfel (
    deconectați($fișier);
    }
    }
    }

    Funcționează exact pe același principiu - dacă un fișier este transmis ca argument, atunci acesta este șters (unlink($file);), iar dacă un director, este deschis, căutat secvențial și pentru fiecare fișier (inclusiv subdirectoare) aceeași funcție se numește dd()...

    Un alt exemplu în care funcțiile recursive sunt foarte convenabile de utilizat este citirea fișierelor prin protocolul HTTP cu urmărirea redirecționării - trebuie să deschideți o conexiune, să solicitați fișierul (sau doar antetul) și să verificați răspunsul serverului - dacă acesta conține antetul

    Apoi trebuie să închideți conexiunea și să apelați din nou funcția de citire a fișierelor, dar cu o nouă adresă. Scrierea unei astfel de funcții poate fi sugerată ca „temă pentru acasă”, dar rețineți că trebuie să o abordați cu atenție - cu siguranță ar trebui să salvați o listă cu toate redirecționările (este convenabil să utilizați o matrice statică pentru aceasta) și să comparați adresele, în caz contrar, funcția poate intra foarte ușor într-o buclă dacă există redirecționări „în cerc”.

    Probabil asta e tot. Sper că utilizarea funcțiilor recursive poate adăuga un alt instrument puternic la „arsenalul programatorului”...

    Sunteți de acord că foarte des atunci când scrieți un site web aveți nevoie de anumite blocuri de cod care să fie repetate de mai multe ori (de mai multe ori :-))

    În consecință, regula de programare ne spune: „Dacă o bucată de cod, indiferent de ce - o linie sau mai multe, se repetă de mai multe ori, TREBUIE SĂ SCRIEȚI O FUNCȚIE SUB EA!”

    Aceste. O funcție este o „piesă” de cod de program care va fi executată atunci când este „solicitată”. Cu alte cuvinte, apelăm un anumit cod în mod repetat atunci când avem nevoie de el.

    Un exemplu de funcții este, opțional, un fel de editor grafic, de exemplu Corel. Acolo, programul în sine cântărește foarte puțin, iar codul programului nu depășește câteva pagini (eventual :-)). Orice altceva din el și faptul că poate fi împrăștiat în funcții - apăsat un buton, numit funcție, a funcționat, a finalizat. Apăsat un alt buton - a fost efectuată o altă funcție etc.

    Deci, cum este descrisă o funcție în php?

    Unde test este numele funcției

    Cel mai simplu exemplu:

    Desigur, trebuie să numim acest TEST() cumva. Acest lucru se face foarte simplu:

    Rețineți că testul nostru () nu poate fi declarat de două ori. Aceste. următorul cod va duce la o eroare

    Există câteva mii de funcții încorporate în php și, în mod natural, sunt deja declarate, ceea ce înseamnă că dacă vrem brusc să le declarăm pe ale noastre, de exemplu
    GetType(), atunci codul va produce o eroare.

    Deci, înainte de a ne crea propria funcție, trebuie să verificăm dacă un astfel de nume există deja?

    Acest lucru se face folosind funcția încorporată astfel:

    Aceste. dacă test() există - returnează adevărat, dacă nu - fals

    Parametrii funcției.

    După cum puteți vedea, după numele funcției sunt paranteze, deci sunt necesare pentru ceva :-)

    Putem trece anumiți parametri funcției noastre. Acestea sunt variabile simple, de exemplu trecem variabila $name:

    Deoarece avem un parametru, trebuie să-l transmitem cumva funcției. Acest lucru se face în mai multe moduri:

    Pot exista mai multe argumente. Ele sunt separate prin virgule

    Primim:

    Există o altă modalitate de a apela funcții, care este folosită extrem de rar, DAR trebuie să știi despre asta.

    De exemplu:

    Ce vedem aici? Când php vede o variabilă și imediat o paranteză de deschidere, înțelege imediat că este necesar să apeleze o funcție al cărei nume se află în această variabilă. Este clar?

    Există, de asemenea, parametri obligatorii și opționali. Parametrii necesari sunt trecuți mai întâi!

    Îți amintești masa noastră de înmulțire?

    Să scriem o funcție pentru aceasta, modernizând ușor tabelul, astfel încât să poată fi evidențiat în culoare și îngroșat:

    Pe linia 18 apelăm funcția cu parametri impliciti, apoi o apelăm cu parametri diferiți. Să vedem ce s-a întâmplat:

    Asta-i toată magia! :-)

    Returnarea unei valori dintr-o funcție.

    Uneori avem nevoie de funcția pentru a nu executa ceva, a scoate ceva etc. Și a returnat o valoare (un număr), de exemplu, cum ar fi funcția încorporată strlen() care a returnat lungimea șirului. Să ne uităm

    Vă rugăm să rețineți că return nu numai că returnează valoarea funcției, ci și termină execuția acesteia - similar cu break in bucle. Aceste. totul de mai jos returnează în funcție nu va fi niciodată finalizat!

    Apel de funcție recursiv.

    Principiul de funcționare este că funcția se numește singură, adică. recursiunea. Să ne uităm la exemplul unei funcții care calculează factorial (cine a uitat - de exemplu, factorialul de cinci este 1*2*3*4*5 și amintindu-ne că factorialul de zero este 1)

    Sper că totul este clar aici.