Iterace v PHP podobně jako v JS rubrika: Programování: PHP

8 rmaslo
položil/-a 4.12.2014

Vím, že je v PHP objekt Iterator a jak se používá ...
Nicméně nějakou třídu používám v PHP i v JS a asi bych chtěl (když už má stejný data), aby měla i stejný metody.

Je proti něčemu tohle ?

class ExtendedArray
{
private $data = array('a'=>1,'b'=>2,'c'=>3);
private $neco;
//....
public function each ($fn)
  {
  foreach($this->data AS $k => $v)
    $fn($k, $v);
  }
}//End ExtendedArray
 
$x = new ExtendedArray();
$x->each(function ($key, $val)
  {
  echo $key.':'.$val.'<BR>';
  });

Vím, že ta kulatá závorka na konci někomu může připadat extra hnusná, ale u mě je spíš žádoucí.
Spíš mi jde o to, jestli někdo nemá nějakou výtku typu alokace paměti, naprosto extrémní pomalosti atd...

Komentáře

  • roman.hocke : Pokud většina tvé aplikace používá "tu samou" třídu na serveru i na klientu, zvažoval jsi použití server-side javascriptu? Mohl bys pak tu třídu sdílet na serveru i klientu a nemusel ji psát dvakrát ve dvou jazycích. 4.12.2014
odkaz Vyřešeno
9 Vašek Ch.
odpověděl/-a 4.12.2014

Není to vůbec proti ničemu. Efektivita programátora a přehlednost kódu ušetří více než náklady na vyšší HW kvůli více CPU cyklům. A bottle necky aplikace bývají stejně ještě někde úplně jinde.

Pro zajímavost, my máme třídu ArrayList (podobná jako tvoje ExtendedArray) s metodami jako each(), map(), filter(), getFirst(), getOnly() a dalšími a pracuje se s tím úžasně.

Např. místo

/** @var Product $product */
foreach($products as $product) {
  // ...
}

můžeme udělat jen

$products->each(function(Product $product) {
  // ...
});

Nemůžeme si to vynachválit. Dokonce máme i obdobnou třídu, která bere jako klíče jiné objekty (interně se používá spl_object_hash()) a nenarazili jsme reálně na problém s výkonem ani u tohodle.

Komentáře

  • rmaslo : Praktická zkušenost mě fakt potěšila :-) Mě se to teoreticky docela líbí, akorát jsme se bál jestli v tom není nějaký zakopaný háček. Ok jdu do toho taky. 5.12.2014
  • Michal Kleiner : Jak píše Vašek, lidské zdroje jsou dražší a přehlednost a čitelnost se vyvažuje těžko, takže pár bajtů paměti nebo pár cyků CPU za to určitě stojí, pokud se jedná v podstatě "jen" o ten zápis. Pokud by ses snažil vynalézat kolo, asi by to bylo o něčem jiném, ale tohle bych zařadil do kategorie "syntactic sugar" nebo pomocné 'funkce'. 5.12.2014
  • rmaslo : @Vašek Chromický: Začal jsme refaktorovat - a jistou mouchu to má: V tom cyklu se typicky pracuje s mnoha proměnnýma zvenčí. Např. Nejprve se venku inicializují, pak přijde cyklus něco se do nich poskládá a po cyklu přijde třeba jejich implode. Proměnný zvenčí si předávám přes use(...). Ale teď jsme psal funkci kde jsem si přes use musel předat dvě proměnný odkazem a další tři hodnotou ... a to je už fakt dost. Nemáte na to nějakou fintu? Přece jenom viditelnost proměnných v PHP a v JS je v tomhle dost rozdílné. 7.12.2014
  • Honza Břešťan : Trosku si zakituju, ale pokud ta iterace potrebuje closure na 5 promennych zvenku, mozna toho dela moc - a cela ta metoda kolem taky. 7.12.2014
  • Kit : @Jan Břešťan: Souhlas :-) 7.12.2014
  • Vašek Ch. : Pokud chceme z cyklu něco vrátit, používáme většinou metodu, která vrací array všech návratových hodnot callbacku. Tím opadá režie okolo starání se o návratovou hodnotu. Pánové ale správně naznačují, že toho možná cyklus dělá vážně hodně, padž my se málokdy dostaneme k více než dvěma usům a předávání odkazem jsme nemuseli řešit asi nikdy. 7.12.2014
  • rmaslo : Ok - asi máte pravdu. Co dělá vnitřek cyklu: Z pole {Table:firma, Field:id} složí ____`firma`.`id` přičemž zkontroluje existenci fieldů. Hodnotou předávané parametry: 1. Struktura db ve formě dvoudimenzionálního pole: první úroveň tabulky druhá - pro každou tabulku všechny fieldy. 2. Pole se seznamem AS tabulek. 3. Počet mezer před složeným stringem (pro vnořené dotazy). Odkazem předávané: Sběrné pole kam výsledky zapisuji stylem SbernePole[]=... a které po skončení cyklu imploudnu. Analogické "Sběrné pole chyb". Celý vnitřek toho cyklu má 5 příkazů (a celá metoda asi 10 řádek), což na první pohled vypadá ok. Ale asi je pravda, že ty cykly by principiálně měly být dva. V prvním kontrola ve druhém skládání. Pak by ten první měl: odkazem pole chyb a hodnotou Strukturu a seznam AS tabulek. Ten druhý by měl: odkazem sběrné pole složených výsledků a hodnotou počet mezer. Samozřejmě, když jsou pod sebou dva cykly (projíždějící to samý pole), kde jeden má tři řádky a druhej dva řádky, tak je v rámci optimalizace jde dát do jednoho cyklu. Ale už to není "principiální řešení", ale na "rychlost optimalizované řešení" a pak se je porušen princip max. tří parametrů funkce, což by v principiálním být nemělo, ale v optimalizovaném může. 7.12.2014
  • rmaslo : @Vašek Chromický: Velmi zajímavý nápad. Je mi asi jasný, že místo public function each ($fn) { foreach($this->data AS $k => $v) $fn($k, $v); } máte něco jako public function each ($fn) { foreach($this->data AS $k => $v) $returns[] = $fn($k, $v); }, ale není mi jasný kam si to $returns uložíte do té doby než ho vyzvednete tou metodou. Do $this ? nebo to jde nějak šikovněji ? 7.12.2014
  • Honza Břešťan : @rmaslo: Nebo to nechat v jednom cyklu, ale zabalit souvisejici zodpovednosti do objektu, ktere se v tom cyklu zavolaji. Tak, jak je to popsane, ma porad jedna metoda zodpovednost za kontrolu i skladani, i pokud to bude ve dvou cyklech za sebou. Pokud se to oddeli do objektu, staci zavolat ty objekty, kterym se predaji prvky toho pole. Zodpovednost te obalujici metody bude jen predani dat, orchestrace a vraceni vysledku. 7.12.2014
  • Honza Břešťan : Jinak k tomu vraceni - pole $returns muze byt lokalni promenna pred tim cyklem (a dalsi closure pro ten callback), ne? Ale pokud je to mozne, spis bych pouzil generator: public function each ($fn) { foreach($this->data AS $k => $v) yield $fn($k, $v); }. Ma to sice overhead iteratoru, ale cte i pise se to dobre a neni potreba ho implementovat rucne. 7.12.2014
  • rmaslo : @Jan Břešťan: ad. Dva objekty v jednom cyklu ... jasný rozumím souhlas. 8.12.2014
  • rmaslo : Ad. $returns: S tím se dá čarovat celkem dost. Ale asi si napřed musím ujasnit zadání. Protože někdy se používá "return false" v té annonymní funkci jako break ve foreach. Což samozřejmě tak trochu koliduje s tím, že by se ty returny měly schovávat. Takže jedna možnost je postavit zadání takto: Před Each řeknu objektu co vlastně znamená ta návratová hodnota z té anonymní funkce (a jestli teda schovávat nebo false brát jako break atd...) Druhá možnost je postavit to zadání takto: Nemusím nic zadávat a vždy schovávam. Pokud vrátím speciální objekt třídy "Break" tak končím cyklus. Pokud vrátím speciální objekt třídy "Continue" tak výsledek zahazuju a v cyklu jedu dál. Pokud si výsledky iterace nevyzvednu do konce funkce v které iterace byla volána tak je zahodim. 8.12.2014
  • Honza Břešťan : Tady uz se ukazuje, ze jeden spolecny each je nevhodna abstrakce. Samotna each metoda je proste iterace, tam asi neni problem pouzit to vraceni false. Pokud to ale ty prvky transformuje, tak je to klasicky map. Kdyz se to takhle rozdeli, odpada to (podle me ne uplne pruhledne) rozhodovani, jak se ma implementace zachovat. V souvislosti s tim me napada jeste jeden zasadni rozdil: each bude vzdycky eager, nedava smysl neco lazy iterovat, kdyz nam jde primarne o sideeffect. Naopak map s generatorem muze byt lazy - pak treba s pomoci neceho jako Standard Query Operators muzu postavit nad kolekcemi slusne embedded DSL jako treba nad list monadou (filtering, projecting, partitoning, grouping...). Potom kdyz budu chtit ukoncit iteraci driv, pouziju takeWhile, kteremu dam krome predchoziho iteratoru jeste podminku, dokud se ma iterovat (nebo si napisu takeUntil s obracenym vyznamem). 8.12.2014
  • Taco : @rmaslo, @Jan Břešťan: map, filter, fold :-) 8.12.2014
  • Honza Břešťan : @Taco: pokud chci byt minimalista, staci mi return a nejaky katamorfismus, ale spis chci pouzitelne API :) 8.12.2014
  • rmaslo : @Jan Břešťan: Taky jsem dospěl k závěru, že jeden each je asi málo a zrovna přemejšlel jak ten "druhej each" nazvat ... když jsme si přečetl Tvůj příspěvek a rozsvítils mi - map :-) . Eager x Lazy chápu, ale teď asi nevyužiju. Ano v podstatě stavim DSL a jako fyzický nosič těchto jazyků používám kolekce. Ale tento DSL nespouštím, ale překládám a pak spouštím ten přeložený výsledek. takeWhile jsem pochopil Each/Map rozšířený o další parametr v kterém je zase annonym. funkce, která tu iteraci může ukončit. A na slovo katamorfismus koukám jako puk. Jinak fakt Velký plus za znalosti který máš - někdy mám dojem jakobys mi odpovídal na otázky který mě napadnou až zejtra. 8.12.2014
  • maryo : Anebo prostě naimplementovat ten iterátor :)... I když na ten map apod. bys stejně potřeboval iterator_to_array, takže to taky není úplně ono. 10.12.2014

Pro zobrazení všech 7 odpovědí se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.