Procházení polí

Školení, která pořádám

K procházení polí slouží v PHP konstrukce foreach a není důvod používat jinou. Tato konstrukce je k procházení polí navržená a je proto efektivní, navíc má tu výhodu, že pracuje nad kopií pole, takže změny v původním poli ji nijak neznervózní.

<?php
// správný způsob
foreach ($pole as $key => $val) {
    echo "<li><a href='?id=$key'>$val</a></li>\n";
}

// v PHP 3 se používala tato konstrukce, kterou leckde vidím dodnes
while (list($key, $val) = each($pole)) {
    echo "<li><a href='?id=$key'>$val</a></li>\n";
}
?>

Druhá konstrukce mi přijde nepřehlednější, navíc funkce each pracuje pouze s proměnnou, takže jí nelze předat např. výsledek funkce nebo pole přímo vytvořené funkcí array.

V PHP 5 je k dispozici rozšíření konstrukce foreach, které umožňuje snadno měnit prvky procházeného pole:

<?php
// kód pro PHP 4
foreach ($pole as $key => $val) {
    $pole[$key] = 2*$val;
}

// kód pro PHP 5
foreach ($pole as &$val) {
    $val = 2*$val;
}
?>

V tomto případě je samozřejmě nutné procházet proměnnou a ne obecný výraz.

Jakub Vrána, Výuka, 21.2.2005, diskuse: 31 (nové: 0)

Diskuse

timmy:

"...není důvod používat jinou..."

a co když nevím, jestli dané pole existuje. v případě while cyklu stačí dát před each zavináč (@each(...))

je mi jasné, že to mohu otestovat IFem, ale v případě while+each nemusím.

ikona llook:

A před foreach nejde napsat zavináč? Vlastně to nevím jistě, protože mě to ještě nenapadlo (existenci radši ověřuju isset), ale myslím, že by to mělo jít.

ikona Jakub Vrána OpenID:

Vlastností elegantního kódu by také mělo být, aby byl dobře čitelný.
<?php
if (is_array($pole)) {
    foreach ($pole as $val) {
        // ...
    }
}
?>
je lépe čitelné než
<?php
while (list($val) = @each($pole)) {
    // ...
}
?>
protože na první pohled není patrné, co jsem tím @ chtěl říct.

ikona spaze:

pripadne se to da resit i pomoci

<?php
foreach ((array)$array as $value) {}
?>

HonzaM:

No, to ale není úplně to samé - pokud v $array bude nějaká hodnota (byť $array = ""), pak
<?php
 
foreach ((array)$array as $value) {}
?>

jeden cyklus vykoná, narozdil od

<?php
if (is_array($pole)) {
    foreach ($pole as $val) {}
}
?>

Jiří:

Vypsat obsah pole to ano, ale spíše by se hodil popis jak pracovat s polem, u něhož dopředu neznám počet řádků.

ikona dgx:

>> timmy

jsem také pro jednoduchost, ale používání @ je dobré se zcela vyhnout. Může to "utlumit" i jiné chybové hlášky a pak se v programu špatně hledají chyby.

Jan Mensik:

radit pouzivat foreach misto while(each) mi prijde velmi zacatecnicke, to snad napadne (skoro) kazdeho, ne? Navic je popis fungovani foreach pospan v kazdem zacatecnickem tutorialu.

Co treba psat o praci s objekty v PHP4, kde je pristup k nim ne uplne stastny a nekdy problematicky.

joe:

foreach je vynikajici reseni ve spojeni s rozhranim Iteratoru objektu (http://cz.php.net/manual/en/language.oop5.iterations.php).
Jinak presne jak pisete :) .. zmeny v poli tuto funkci nikterak neznervozni, coz muze v nekterych pripadech vadit. funkce list (take napr. compact a extract) je velmi zajimava a ja ji casto pouzivam. Proto nejsem rad, kdyz ji nekdo nerad vidi, nebo se hned snazi odstranit.

NeNe:

Co se posledního příkladu týče, IMO je přehlednější varianta s $key než s odkazem.

caracho:

Mezi foreach a while je podtatny rozdil! Foreach pracuje s kopii pole, a pripadne zmeny v nem jsou pak zahozeny.

Zax:

Pokud foreach pracuje s kopii pole, kolik potom stoji tato rezie navic - vytvoreni kopie pole v pameti a jeho zruseni po skonceni iteraci ? Melo by to tedy byt pomalejsi zvlaste pro velka pole ?

ikona Jakub Vrána OpenID:

Ruku do ohně za to nedám, ale řekl bych, že skutečná kopie pole se vytváří až v momentě jeho změny, do té doby to je jenom odkaz na původní pole.

dmc:

od profesionala bych ocekaval ze namisto hlasky "nedam ruku do ohne" sahne radeji po odbornych zdrojich nez se k necemu vyjadri ;-)

ikona Jakub Vrána OpenID:

Teď už za to ruku do ohně dám :-). PHP kopii hodnoty vytváří skutečně až v okamžiku změny.

dmc:

fajn ;-)

ikona v6ak:

Dodám, že experimentálně ověřit to je možné pomocí v6_Zval::isIdentical z http://gist.github.com/321601 .

Na odkazu je spíše takový experiment, který může pomoci ukázat, jak to v PHP s proměnnými chodí.

Mimochgodem, psal jsem ho v době, kdy jsem zvažoval java bytecode->php překladač a přemýšlel jsem o vyhnutí se obálkovým typům typu java.lang.Integer. Jako gist jsem jej zveřejnil nedávno.

RATMex B:

To sa mi snad zda! Nahodou som objavil tuto stranku a po precitani informacii o autorovi som sa potesil, ze je tu konecne niekto, kto sa do PHP vyzna do hlbky. Usudzoval som tak z informacie, ze pan Vrana je spoluautorom manualu, preto som logicky predpokladal, ze sa vrtal aj v zdrojakoch a pod. a teda do podrobna ovlada celu strukturu a principy prace PHP.
Moje nadsenie vsak opadlo hned po precitani prveho clanku, t.j. tohto. Konkretne prva veta: "K procházení polí slouží v PHP konstrukce foreach a není důvod používat jinou." ma skoro zhodila zo stolicky.

Teda mozno nemam zdaleka take skusenosti ako vy, ale aj taky podpriemeny pouzivatel PHP vie, ze foreach je zo vsetkcyh moznych alternativ po "while ... each" ta najhorsia aka je k dispozicii. Nie len, ze exituje dovod pouzivat ine sposoby, ba dokonca je to nevyhnutne, v zaujme dobreho kodu, t.j. zanika dovod pouzivat foreach ako taky.

Pre vsetky(!) mozne sposoby pouzitia klucov a prvkov pocas iterovania, ktore ma teraz napadaju (t.j. 1. iba citanie, 2. zber kopii, 3. zber referencii, 4. modikifacia) predstavuje vytiahnutie klucov do extra pola znacne zrychlenie! Ak sa k tomu vytiahnu aj samotne hodnoty, tiez sa cyklus urychli a rozdiel od foreach prehlbi.

To, ze sa pracuje na kopii nie je vyhoda, ale prave naopak nevyhoda, lebo to tiez predstavuje zbytocnu reziu. Ak by som chcel pri manualnom interovani pouzit kopiu, tak mi nic nebrani.
A aj pokial sa jedna o zmenu obsahu pola, tak drviva vacsina modifikacii nema na iterovanie vplyv - zmena obsahu lubovolnej polozky, zmena kluca spracovanej a akurat spracovavnej polozky, ostranenie kluca spracovanej a spracovavnej polozky.


Idem si precitat este dalsie clanky, ale po tomto prvom im vela sanci nedavam. :(

ikona Jakub Vrána OpenID:

Výhoda foreach je hlavně v lepší čitelnosti a předvídatelnosti kódu. K pochopení while (list($key, $val) = each($pole)) je potřeba znát spoustu věcí: pointery v poli, nezvyklé chování funkce each, nestandardní až nečistou konstrukci list. Když se k tomu připočte možné zacyklení v případě modifikace procházeného pole, je skutečně lepší se tomuto způsobu vyhnout - pokud nemáte sklony místo PHP používat třeba Perl.

Z článku http://php.vrana.cz/optimalizace-kodu.php odvodíte, že hrubému zrychlení v řádu pikosekund valný význam nepřikládám. Navíc vzhledem k tomu, že PHP používá tzv. "lazy copy" (tedy fyzická kopie se provádí až když dojde k modifikaci dat), tak kopírování pole žádnou významnou režii nepředstavuje (je to v podstatě inkrementování čítače zachycujícího, kolik se na objekt odkazuje referencí).

RATMex B:

Ved prave "while ... each", som zamietol este viacej ako foreach. Co som daval do popredia je pouzitie "machanickeho" spracovania, t.j. vytiahnut si hlavne kluce a popripade aj hodnoty von.
Co sa tyka citatelnosti, tak tam vobec nehraju rolu nejake 2-3 riadky, ale stabna kultura + popripade komentare. Komu robi toto problem, tak tam nepomoze ani foreach.

Clanok optimalizacii som si precital, ale bohuzial jeho gro sa netyka kontrukcie foreach. V tomto pripade sa nejedna o "setrenie pikosekund", ale o znizovanie casovej zlozitosti kodu tym, ze sa pouziju ine vstavane konstrukcie a funkcie, ktore maju danu zlozitost mensiu.
V tomto pripade sa jedna o zlepsenie, pri ktorom sa s rastucim objemom dat rozdiely v casovej narocnosti zvacsuju rychlejsie ako konstantne (O(log n) ak som nahodou na nieco nezabudol).

ikona Jakub Vrána OpenID:

Možná jsem přesně nepochopil, co máš na mysli obratem "vytáhnutí klíčů do extra pole". Zkus napsat celý kód primitivního příkladu v článku a třeba si porozumíme.

Pak třeba budeme moci probrat i to zrychlení O(log n).

RATMex B:

$arr - asociativne pole

verzia 1 (foreach):
foreach ($arr as $key => $value)
{
  kluc iterovaneho prvku - $key
  hodnota iterovaneho prvku - $value
}

verzia 2 ("vytiahnutie klucov"):
$arr_keys = array_keys($arr);
$arr_size = count($arr_keys);
for ($i = 0; $i < $arr_size; $i++)
{
  kluc iterovaneho prvku - $arr_keys[$i];
  hodnota iterovaneho prvku - $arr[$arr_keys[$i]];
}

verzia 3 ("vytiahnutie klucov a hodnot"):
$arr_keys = array_keys($arr);
$arr_values = array_values($arr);
$arr_size = count($arr_keys);
for ($i = 0; $i < $arr_size; $i++)
{
  kluc iterovaneho prvku - $arr_keys[$i];
  hodnota iterovaneho prvku - $arr_values[$i];
}

O danom zrychleni som hovoril vo verzii 3.

ikona Jakub Vrána OpenID:

Přesně jak jsem očekával - zanedbatelného zrychlení skriptu za cenu dramatického snížení jeho čitelnosti a přehlednosti. Ve výjimečných případech, kdy se hodí každé procento výkonu, má používání takovýchhle konstrukcí smysl, v běžném případě ale ne.

crook:

HI no tvou trojku bych chtel videt v rozsahlem projektu kde na nem pracuje vice lidi. Byli by asi nadseni:) (prosim neber to zle)

RATMex B:

Este by som dodal, ze som si precital uz aj dalsie clanky a v podstate sa tuto jedna iba o mensi prvotny sok a chybicku krasy. Inak na mna zatial precitany zvysok posoby velmi pozitivne a profesionalne a kvalitne.

@ss@ssIn:

For je v tom, ze niekdy sa neda pouzit foreach (napr. ked prechadzas jedno pole a zrazu potrebujes prejst na ine a potom sa vratit presne tam kde si bol ), takze obcas array_shift/pop, next, prev, end:)

Jakube vies na co teraz myslim ;p ?

ikona Marty:

Dostal jsem se do situace, kdy je poměrně pohodlné použít přiřazování:

$pole[$pravo][$oblast_prava][$vlastnost][] = $hodnota;

Neví někdo, jaké má toto úskalí? Přecejen je to už "pořádné" pole.

dmc:

V dodatku o php5 rozsireni by bylo dobre ponekud vice zduraznit skutecnost uvedenou hned na zacatku, ze foreach pracuje s kopii pole, tudiz i ty -php5- zmeny jsou platne tak akorat v ramci cyklu foreach, pote se kopie samozrejme zahodi a original neni nijak dotceny.
Predpokladam ze tento clanek je urceny pro lamy, bfu a zacinajici programatory - tudiz by nemel pripadne ctenare mast, naopak by svou polopaticnosti mel pomahat odstranovat veskere zacatecnicke nejistoty ;-)

ikona Jakub Vrána OpenID:

Zkus si kód spustit, přehlédl jsi ampersand a ukázku jsi tedy nepochopil.

dmc:

omlouvam se, prehlednul jsem &, tudiz muj predchozi prispevek byl bezpredmetny

Michal:

Pokud jeste nekde pouzivate/uvidite konstrukci

while (list($key, $val) = each($pole))

tak velký pozor, zde se jiz nekde/nekdy predtim pole neprochazelo, je to zdroj skvelych chyb k hledani

je potreba pouzit reset($pole);

http://stackoverflow.com/questions/3304885/…-as-key-value

Vložit komentář

Používejte diakritiku. Vstup se chápe jako čistý text, ale URL budou převedeny na odkazy a PHP kód uzavřený do <?php ?> bude zvýrazněn. Pokud máte dotaz, který nesouvisí s článkem, zkuste raději diskusi o PHP, zde se odpovědi pravděpodobně nedočkáte.

Jméno: URL:

avatar © 2005-2018 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.