Autor Zpráva
nethor
Profil
Zdravim, řeším stránkování;
mám fci , která má v závislosti na vyhodnocení podmínky přeskočit krok nebo zastavit cyklus ( continue / break) . (.. nebo nechat běžet )
Narazil jsem na 2 problémy:

1) Proč nefunguje tohle?:
for ($i=0;$i<=10;$i++) {
    eval("break ;")    ;
}
// Fatal error: Cannot break/continue 1 level in ... : eval()'d code on line 1
Vždyť JE to uvnitř cyklu, příp. jak to udělat?


2)

Kyž se podaří vyřešit problém 1) , chtěl jsem použít toto:
foreach ($this->Data as $Row) {
    eval($this->Pager->Skiper())     ; //  fce vrací [ "break  ;" / continue  ;  / false ]
     /* ....  */
}

Jde to nějak udělat bez eval() ? .... Abych volal pouze $this->Pager->Skiper() ;
juriad
Profil
nethor:
1) Protože break se váže k cyklu na syntaktické úrovni. A vyvaruj se jakémukoli použití evalu. Vždy to lze vyřešit jinak, rozumněji.

2) Nelze. Vracej si nějakou hodnotu, třeba řetězce "break", "continue" nebo null. A pak se v podmínce rozhodni, co chceš provést. Vyvaruj se switche, protože ten by ti přidal jednu úroveň breaků, které by ses nepěkně zbavoval.

To si jako představuješ, že samotné volání funkce má mít vliv na okolí místa, ze kterého byla zavolána? To je fakt prasárna.

A proč vůbec potřebuješ break a continue? Nemůžeš si hranice cyklu vypočítat předem a přesně?
Joker
Profil
nethor:
1) Proč nefunguje tohle?:
Hm, tak z pohledu čisté teorie (dokumentace) se eval vyhodnocuje v aktuálním kontextu, takže by něco takového fungovat mohlo. Jestli to nefunguje, asi to bude nějakou v dokumentaci nezmíněnou vlastností implementace evalu.

Z praktického pohledu je škoda, že PHP v takové situaci může leda tak vyhodit fatal error a ne tomu programátorovi nafackovat, aby si pamatoval, že takoé zvěrstvo nemá dělat.

Jde to nějak udělat bez eval() ? .... Abych volal pouze $this->Pager->Skiper() ;
Jistě.
Tak za prvé bych upravil metodu Skipper, aby vracela něco normálního. Řekněme true pokud se má přeskočit a false pokud ne. Taky bych pro ní asi vymyslel lepší název, ale to teď necháme.
A teď pozor na tu magii:
foreach ($this->Data as $Row) {
  if (!$this->Pager->Skipper()) {
    // kód
  }
}

Kdybych měl ten druhý (v příspěvku v pořadí první) příklad a chtěl tři stavy: Pokračovat normálně, přeskočit jednu iteraci a ukončit celý cyklus:
Řekněme, že budu mít metodu IsFinished, která vrací true, pokud mám ukončit celý cyklus, a metodu IsSkipped, která vrací true, pokud mám přeskočit iteraci.
for ($i = 0; !$this->IsFinished() && $i < 10; $i++) {
  if (!$this->IsSkipped()) {
    // kód
  }
}

Opět žádná věda, možná jediné co na tom není triviální je uvědomit si, že podmínka ve for cyklu je výraz a nemusí tam být pouze porovnání indexu proti něčemu.
Kubo2
Profil
nethor:
if($this->Pager->skipper() === SKIPPER_SKIP)
   break;
else if($this->Pager->skipper() === SKIPPER_CONTINUE)
   continue;

// else

Ale ako už spomenul juriad, lepšie je si dopredu vypočítať fixné hranice cyklu a tie potom použiť.
nethor
Profil
jo, jasně takhle jsem to nakonec obešel:
         
foreach ($this->Data as $Row) {
    $Skipper = $this->Pager->Skipper()     ;
    if        ($Skipper == "break")         break    ;    
    elseif($Skipper == "continue")     continue    ;    
// ...
}    

Řeším to z důvodu snadného použití.
Chtěl jsem, aby uměla řídit běh cyklu přímo třída, protože když pak budu chtít použít stránkování jakémkoliv výpisu cyklem, byla by to záležitost na 3 řádky:
$this->Pager = new Pager($this->Data)    ;

$echo[] = $this->Pager->Html()    ;

$this->Pager->Skipper()     ;
Samozřejmě na správná místa...

Asi správné řešení je tedy upravit data pro výpis ve třídě Pager() a cyklus spustit s nimi:
foreach ($this->Pager->Data() as $Row) {}

... ale to zase vytvoří další proměnnou a alokuje kus paměti, u rozměrnějších dat se to může projevit na rychlosti a stabilitě.


PS:
Joker:
... s těma fackama snad šetři, jenom jsem se zeptal, když jsem na Google nenašel ... učím se ...
Joker
Profil
nethor:
s těma fackama snad šetři
Pokládal jsem za nezbytné zdůraznit, že ten původní způsob byl opravdu hodně špatný.

takhle jsem to nakonec obešel
Tak by to už šlo, ale asi bych zvážil rozumnější návratovou hodnotu pro metodu Skipper. Nebo minimálně použít konstanty, jako to má [#4] Kubo2.

Chtěl jsem, aby uměla řídit běh cyklu přímo třída, protože když pak budu chtít použít stránkování jakémkoliv výpisu cyklem, byla by to záležitost na 3 řádky
A to je právě kořen toho špatného nápadu.
Sice by se ušetřily dva řádky kódu, ale vznikl by extrémně nečitelný a záludný kód.

Řekněme, že by něco takového bylo možné, vzniklo by např.:
foreach ($data as $row) {
  skipper();
  neco();
  necojineho();
}

…přičemž skipper by kontroloval chování cyklu, což ale při pohledu na ten cyklus není žádným způsobem patrné!
Kdokoliv by ten kód upravoval nemá bez prozkoumání kódu ve skipper() šanci poznat, že neco() a necojineho() se nemusí provést vždy.

Jde o příklad chyby v návrhu programu, které se říká Action at a distance (český překlad mě teď nenapadá).
Tori
Profil
Joker:
"Action at a distance" - díky, nevěděla jsem, že se to nějak jmenuje. Česky možná: dálkové ovládání, nečekané důsledky, řízeno z vesmíru, panenka voodoo, ...

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm: