Získání prvku z pole vráceného funkcí

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

Pokud funkce vrací pole, může nás z něj zajímat jen jeden konkrétní prvek. Vzhledem k tomu, že PHP na rozdíl třeba od JavaScriptu nepodporuje syntaxi f()[0], používá se nečastěji uložení pole do pomocné proměnné a získání prvku z ní:

<?php
$ar = f();
echo $ar[0];
?>

Pokud je v kódu takovýchto operací hodně (např. chceme vždy první prvek několika XPath dotazů), stává se kód nepřehledný. Pro získání prvního prvku můžeme použít funkce reset, ta ale parametr přijímá referencí, takže i když konstrukce reset(f()) v PHP 5.2.1 funguje, tak je možné, že v budoucích verzích fungovat nebude. Proto je jistější napsat si vlastní funkci, která nám navíc může vrátit libovolný prvek:

<?php
/** Vrácení daného prvku pole, obdoba $array[$key] funkční i pro pole vrácená funkcí
* @param array pole s prvky
* @param mixed klíč požadovaného prvku pole, výchozí hodnota je 0
* @return mixed požadovaný prvek pole
* @copyright Jakub Vrána, https://php.vrana.cz/
*/
function array_item($array, $key = 0) {
    return $array[$key];
}

echo array_item(f());
?>

Pokud v poli požadovaný klíč není, vrátí PHP standardní chybu úrovně E_NOTICE, ve funkci místo ní ale můžeme třeba vyhodit výjimku nebo uživatelské varování. Testování musíme provést funkcí array_key_exists, protože isset vrátí false i na prvky pole obsahující null.

Jakub Vrána, Řešení problému, 5.3.2007, diskuse: 51 (nové: 0)

Diskuse

ivan_d:

Je to nehezké řešení nouze, kterou nehodlá řešit Zend. Argument, že foo()[0] znepřehledňuje kód vidím jen jako hloupou výmluvu (podobně jako pojmenované parametry). Škoda.

Martin:

Smarja, pojmenovane parametry by byla pekna hruza...

ivan_d:

<?php

function set($prvni=1, $druhy=2, $treti=3, $ctvrty=4, $paty=5){

  doSomething($prvni, $druhy, $treti, $ctvrty, $paty);

}

set(paty=3);

#opravdu hruza, oc prehlednejsi je (vcetne komentare):

#krome pate hodnoty jsou vsechny defaultni

set(1, 2, 3, 4, 3);
?>

Nehlede ze pokud zavolate set n-krat a potom, se rozhodnete zmenit defaultni parametry, muzeme tipovat, kolikrat na to zapomenete. Nebo se pouziji konstanty (define, const) - ale, to uz bude slozitost kodu zase jinde.

Martin:

Parada, hlavne si pamatovat, ze parametr funkce s anglickym nazvem "set" se jmenuje "paty" a ne "fifth", a nikdy ten parametr ve funkci neprejmenovat... Proste aby by to "jen zjednodusilo" nekolik prikladu, nepovazuji za dostatecny duvod, protoze by to celkove udelalo vice bordelu nez uzitku.

ikona dgx:

Vtip je v tom, že PHP to lze poměrně dost snadno obejít:
<?php
function set($args){
  extract($args);
  doSomething($prvni, $druhy, $treti, $ctvrty, $paty);

}

set(array('paty'=>3));
?>
Jde o použití jednoho slova array() navíc.

ivan_d:

Jistě téměř vše lze obejít - ale to (dle mého názoru) zbytečné úsilí. Navíc s tímto řešením padá Martinův argument o přejmenovávání parametrů. Proč by tedy nemohla tahle operace (a jiné) být ve standardní jazykové výbavě?

ikona MiSHAK:

Dost by se tím pak zanesl jazyk samotný.

.:

Co tim chcete konkretne rict?

ikona finc:

To si zrovna také nemyslím. Co se týče pamatování si, jak jsem to či ono pojmenoval, je snad už pryč, ne? Máme dobré IDE, které nám pomáhá a je schopno refactoringu :)
to Martin: To je to samé jako kdyby jsi tvrdil, že v objektově napsaném projektu nejsi, přes poznámkový blok, schopen zjistit názvy metod jednotlivých tříd ;)

ikona MiSHAK:

Je to jen můj osobní názor: Příjde mi to jako plýtvaní prostředky. Většina IDE zobrazuje popisky parametrů funkci, takže tohle je skoro rozežranost. (Berte to vážně jen z 50%)

ikona finc:

No tak nevím, napadá mě snad jen otázka, co používáš za editor  na PHP?
IDE není jen editor a i když je pravda, že je to dost často hw náročná aplikace, umožňuje natolik zjednodušit vývoj, že dobré ovládnutí IDE (Eclipse, ZEND Studio, ...) považuji za základ při vývoji aplikací.
Nedovedu si představit, jak bych v nějakém poznámkovém bloku používal např. objekty.
Programování není o tom, znát zpaměti názvy funkcí, tříd, či jejich metod a jejich návratové hodnoty. K tomu mi slouží IDE (či manuál), aby mi, podle mých základních parametrů dohledal co potřebuji.

ikona MiSHAK:

Je to už hodně OT :) PSPad/3WE jsou to jen vylepšené notepady (v případě PSPadu hodně vylepšený).

Jo pravda pamatuji si většinu funkcí a objektů co právě používám.

Myslel sem to tak, že IDE zobrazují v hintících parametry fcí. objektů atp. proto mi příjde zbytečné kvůli jednoduchosti psát takový kód. Ale pravda na ničem větším než v řádu (desítek)tisíců jsem nepracoval a uznávám chybí mi tyto zkušenosti.

ikona dgx:

Jazyk je dobrý tehdy, pokud je dobrý sám o sobě, a nemusí se spoléhat na berličku v podobě IDE.

ikona finc:

Kvalita IDE je přímo úměrná tomu, jak je navržen daný jazyk. Nikdy jsem netvrdil, že IDE je nějakou berličkou. Tvrdím, že je to nástroj pro vývoj. Berličkou mohou být různé kusy kódu, které mi umožňují provádět to, co jazyk přímo nepodporuje.
Žádné IDE na světe, ať už pro jakýkoli jazyk, za mě nebude programovat :)
Na projekt o 3 souborech mi jistě bude stačit např. PSPad, ale co když potřebuji větší projekt, třeba o 100 tříd, kde 1 class = 1 file. Nemyslím, že by v tomto případě bylo možné vyvíjet bez vlastností, které mi dnešní IDE nabízejí. Myslím, že nemá cenu tady ty vlastnosti vypisovat, jistě všichni vědí, co mám na mysli.

Martin:

Ano, neni moc rozdil mezi tim pamatovat si poradi parametru a pamatovat si nazvy parametru. Ale ted jsou proste parametry dany poradim a kazde lepsi IDE s nimi umi pomoct. Sam toho vyuzivam maximalne. Prijde mi jako zverstvo chtit po vyvojarich PHP, aby doplnili podporu syntaxe, ktera prida do jazyka akorat nejednoznacnost a udela v nem jeste vetsi bordel, jen kvuli tomu, abych si zkratil zapis o sedm pismen "array()", do ktereho lze nyni pojmenovane parametry zabalit. A jak by to bylo se soucasnymi funkcemi v PHP? Braly by take pojmenovane argumenty i argmenty podle poradi? To by byl casem luxusni gulas :).

ivan_d: Pokud se rozhodnete zmenit nazvy pojmenovanych parametru, budete muset opravit jejich vyskyty. Pokud se rozdnete zmenit poradi parametru, budete take muset opravit jejich vyskyty. Nemusi to byt jen usetreni prace, ale take prace navic, protoze hlidate navic dve veci misto jedne.

ikona finc:

Jelikož s novými verzemi PHP se dost často počítá jako s nekompatibilní verzí na starší verze, tak bych v tom takový problém neviděl :)
Použití pomocí array(), mě osobně, přijde jako mnohem větší prasárna. Podle specifikace jazyka se přece tvoří dané IDE, takže o implementaci v něm se nemá cenu ani bavit.

to MiSHAK: Proč si práci nezjednodušit? Osobně nemám hlavu na to, abych si pamatoval každou metodu, to je prostě nesmysl.

ikona MiSHAK:

Nevím prostě mi to leze do hlavy líp než třeba vyjmenovaná slova. Zjednodušení práce něco stojí, ať už peněz nebo práce, a já sem strašný lenoch.

ikona Jakub Vrána OpenID:

Tady nejde o těch sedm písmen, ale spíše o standardnost řešení. V současnosti se musím u funkce rozmyslet, jestli bude přijímat parametry pořadím nebo jako pole nebo mix obojího (např. základní pořadím a různá nastavení polem). Pokud by bylo povolené předávání parametrů i jménem, nemusel bych tohle vůbec řešit, navíc by se odstranila režie řešení používajícího array().

Doplním, že u interních PHP funkcí něco jako předávání názvem parametru není ve verzi 5.2 principiálně možné, protože parametry zkrátka žádné jméno nemají - to, co je uvedeno v dokumentaci, nikde ve zdrojovém kódu není použito. Ve verzi 6.0 jsou parametry většiny funkcí již pojmenované, proto by to principielně možné bylo.

ikona dgx:

Problém bych viděl někde docela jinde. Je totiž potřeba si říct, kde KONKRÉTNĚ bych takovou vlastnost využil. A když si ty konkrétní případy zanalyzuju, tak zjistím, že něco dělám špatně já, a není chyba v jazyku.

Zjednodušeně - pokud funkce potřebuje pojmenované parametry, pak je funkce špatně navržená.

ivan_d:

Otázka je, kde je měřítko 'správně' a 'špatně' navržené funkce. Myslím, že to správně/špatně bude platit v kontextu konkrétního jazyka nebo programovacího paradigmatu. Jsou způsoby programování (nebo organizování kódu) v php, které se mi nelíbí, běžně jich neužívám, ale někdy je s výhodou použiji  - třeba jen pro rychlé jednorázové řešení (může to být dočasný záchyt během vývoje). Příkladem může být global. Proč by ale jazyk nemohl být silnější? Kdo toho chce čistě a rozumě využít, toho to posune vpřed, kdo to bude prasit tomu se to třeba vrátí. Komu to nebude vyhovovat, nebude to používat. Ale nesměřovalo by to ke stejně průměrnému kód (trochu nadsazuji :) z rozhodnutí Zendu. Mimochodem souhlasím, že sílu jazyka by nemělo suplovat IDE.

ikona dgx:

Samozřejmě, že by jazyk mohl/měl být silnější. Problém je v tom, že v souvislosti s pojmenovanými parametry existují názory, že by to jazyk udělalo slabším. Proto se nedá jednoznačně říct: "dejme to tam, občas to někdo použije."

ivan_d:

Že by to udělalo jazyk slabším? Můžete k tomu napsat více? Skutečně (=bez ironie) by mě to zajímalo.

ikona dgx:

Zkusme čistě prakticky - nadhoďte nějakou reálnou funkci, kde by se pojmenované parametry hodily, a příklad jejího použití.

ivan_d:

Mějme třídu TimeDelta, objekty jsou konstantní (po konstrukci se nemění). Její využití bude ve spolupráci s třídou pro práci s datem, intervalem apod. Detaily rozhraní nejsou podstatné. Při použití pojmenovaných parametrů by kód vypadal takto:
<?php
class TimeDelta{
  private $secs = 0;
  public __construct($days=0, $secs=0, $min=0, $hours=0, $weeks=0, $years=0){
    #tady se cas prevede na sekudny a ulozi do $this->secs;
  }
}

#volani

$endDate = $beginDate->plus(new TimeDelta(hours=2));
?>

Jasně, že by to šlo napsat takhle (nebo jinak - ve stylu nejčastěji používám dny...), ale proč, že:

<?php
class TimeDelta{
  private $secs = 0;
  public seconds($secs){
    return new TimeDelta(0, $secs, 0, 0, 0, 0);
  }
  #atd...
  private __construct($days, $secs, $min, $hours, $weeks, $years){
    #tady se cas prevede na sekudny a ulozi do $this->secs;
  }
}
#volani

$endDate = $beginDate->plus(TimeDelta::hours(2));
?>
Navíc sice málo využívané ale komplikovanější možnosti ve stylu new TimeDelta(days=1, hours=3) mi připadnou výhodnější v řešení s pojmenovanými parametry.

ikona dgx:

Ano, to je pěkný příklad. Uznávám, že tady by se pojmenované parametry hodily, svůj postoj přehodnotím ;)

ikona finc:

Kdyby v PHP existovala možnost přetěžování metod (přímým způsobem), jistě by si měl pravdu ;)

ikona dgx:

Co je to přetěžování? To je trik, jak obejít některé nevýhody striktně typových jazyků. Ale PHP není striktně typový, takže žádné přetěžování tam nemůže existovat. Vlastně se dá říct, že každá funkce je už z principu přetížená.

ikona finc:

Trik? Obejít nevýhody? Tak to si skutečně nemyslím. Vím, že toto lze použít jen u striktně typových jazyků, ale i v PHP existuje možnost typové kontroly, ve vstupních parametrech na array či objekt, lze použít typovou kontrolu, díky tom si myslím, že po přidání možnosti definovat typovou kontrolu i na  int, char... tak by přetěžování nebyla až taková nemožná věc ;)
Opravdu to není žádné obcházení nevýhod, naopak se domnívám, že je to velice uitečná věc, která mi může ušetřit spousty času.
Př.:
<?php

class A
{

   public function foo(array $a) {
      echo "mam pole";
   }

   public function foo(array $a, array $b) {
      echo "mam dve pole";
   }

   public function foo(Object $o) {
      echo "mam objekt";
   }

}
?>
Myslím, že tímto by se spoustu věcí vyřešilo, i ten problém se vstupními parametry a jejich označením.

ivan_d:

Problém je, že Java (tipuji, že tím směrem pokukujete) má fázi kompilace do bytekódu, v php vám to umře uprostřed běhu - proto tento způsob kontroly parametru osobně nepoužívám. Navíc si nemyslím, že by bylo obecně užitečnější napsat N přetížených funkcí pro zpracovavání většího počtu parametrů. Ono je v Javě často vidět něco jako:

public foo(String a){
  return foo(a, 'DefaultniB');
}
public foo(String a, String b){
  return a + b;
}

což je dle mého názoru nepříjemnější než napsat:

public function ($a='DefaultniA', $b='DefaultníB'){
  return $a.$b;
}

Nehledě na to, že to zpřístupňuje více možností.

ikona dgx:

V PHP je typová kontrola volitelná. Ve striktně typových jazycích povinná. Proto podobná věc nejde bez overloadingu udělat. Proto je overloading obcházením striktní typovosti.

V PHP to není potřeba - stačí vynechat typ.

Jakou by mělo logiku do PHP vnést "nad rámec" určitý prvek striktně typových jazyků a zároveň "nad rámec rámce" vnést prvek, který toto umí obcházet?

Ale nechme overloading být. Já vážně nerozumím, jak to souvisí s pojmenovanými parametry, či snad jak by je to mohlo nahradit. PHP prostě nabízí JINÉ prostředky než Java, takže pokud vám něco Javovského chybí, je dost možné, že to nechybí, jen se to prostě v PHP dělá jinak.

ikona finc:

Není to žádné obcházení. Na základě rozdílných parametrů metody (typ nebo počet) mohu provést úplně odlišný kód, což mi právě může pomoci. Nazvat to obcházením striktní typové kontroly, jsou podle mého dost silná slova a navíc to není jen přetížení na typ parametru metody, ale práve i na ten počet.
Jinak zašel jsem asi už úplně jinam, ale v některých případech, při vyhodnocování, co má mtoda učinit (podle parametrů) to jistě je.
Ne, myslím, že spoustu toho v PHP prostě není :)

ikona dgx:

Nebudu se dohadovat o slovíčkách. Samozřejmě že to je obcházení, obchází se nemožnost předat jedné funkci parametr různého typů, obchází se nutnost stejnou funkci pro různé typy pojmenovat jinak. V PHP lze funkci předat různé typy nebo počty parametrů, takže není potřeba nic víc řešit. Naopak nemusím stejný kód psat vícekrát a případnou konverzi typů provedu hned na začátku funkce.

Podstatné je, že od přetížených funkcí se očekává vždy STEJNÁ funkčnost, jen na jiném datovém typu. Provádět úplně odlišný kód je nepochopením smyslu přetěžování.

ikona finc:

Samozřejme, že nikoli. Když už jsem tolik zaujatý Javou, podívej se např. na komponenty Swingu a jejich metod. Vkládání modelu komponenty je něco jiného, než vložení surových dat, atd. V PHP by jsi musel ve funkci zjišťovat, co vlastně parametr obsahuje a podle toho se zachovat, což zde je bráno již na úrovni návrhu kódu.
Prostě při jiném počtu či jiném typu parametrů, se provádí jiný kód. Samozřejmě jsou výjimky a ty pak čistě předávají parametry stejně nazvané metodě s jinými parametry.
Opravdu se nejedná o žádné obcházení.

ikona dgx:

Ale ten rozdíl je v tom, že zatímco v PHP můžeš, v Javě musíš. V Jave musíš vytvořit pro každou variantu zvlášť funkci, v PHP stačí použít jeden if. Má-li funkce jeden až tři parametry se třemi možnými typy, musíš v Javě vytvořit 39 různých funkcí! V PHP nemusíš, stačí jedna. Díky přetěžování není potřeba pro těch 39 funkcí vymýšlet 39 různých názvů. V PHP máš stále jednu funkci, tak k čemu přetěžování?

ikona finc:

Opět se obávám, že to není zcela pravda. V Jave každý objekt dědí jeden objekt. Jinými slovy řečeno, kromě new Object() m8 každý objekt svého rodiče, kterým v případě nepřítomnosti extends je Object. Díky tomu můžu řící, že parametry metody budou typu Object.

public void foo(Object prvni, Object druhy) {
   if (prvni instanceof MujObjekt) {
       System.out.printl("Toto je moje");
   }
}

ikona dgx:

řeč je o typech (Float, Int, Char, Boolean, Object), ne o objektech.

ikona finc:

Aha, tak to jsem netušil, že se bavíme jen o primitivních dat. typech. Jinak, od verze Java od verze 5.0 umí automatické převody mezi prim. typy.

P.S. Už opravdu naposledy, nechci to tady Jakubovi zaplácávat OT příspěvky.
Sice nejsem žádný expert, ale přijde mi, že každý uvažujeme v jiné rovině., to jestli do metody pošlu Integer, Object, "Strycek Jedlicka", myslím, že je úplně jedno. Všechno se dá nějak zpracovat a jak jsem ukázal, ne na všechno musí být to přetěžování. Navíc počtem metod se neurčuje kvalita kódu. Asi mám raději, když vím, co kam mohu poslat, než při běhu čučet kde se mi vlastně stala chyba.
Už je to vážně OT, takže pokud o tom chce někdo debatovat, tak asi někde jinde.

ivan_d:

Máte na mysli tohle:
<?php
 
function overloaded($a, $b=null){
    if(is_null($b)){
      doAnything($a);
    }else{
      doAnythingElse($a, $b);
    }
  }
?>

vs:

public overloaded(String a){
  doAnything(a);
}
public overloaded(String a, String b){
  doAnythingElse(a,b)
}

Prostě v silně typových jazycích (např. Java) jsou typy parametrů funkce pevně dané, ve slabě typových (např. php) ne. Proto mohou být do funkce poslány libovolné typy parametrů. To co doplnil php5 - možnost vynucovat si typ (jde-li o objekt) považuji za nešťastné řešení - nesplnění typu vyhodí za běhu (což je pozdě) fatal error. Jestli se mýlím a jde si vynutit výjimku, rád se o tom dozvím. Místo toho používám něco takového:
<?php
 
function foo($a){
    #odtud muze vypadnout vyjimka, a tu lze zachytit
    BadTypeException::test('TridaKteraMaByt', $a);
  }
?>

Samozřejmě by se mi líbila nějaká inteligentní podpora otestování typů parametrů - něco na spůsob switch:
function overload($a, $b, $c){
  typeSwitch{
    string, string, int      : foo1($a, $b, $c)
    string, string, MojeTrida: foo2($a, $b, $c)
  }
}
Ale to si cucám z prstu.

anode:

S uvedením PHP 5.2 a E_RECOVERABLE_ERROR to jde řešit. Pokud parametr nesplní typ, není vyvolán E_ERROR (fatal error), ale právě zmíněný E_RECOVERABLE_ERROR, který jde ošetřit. A mimochodem, ošetřit to šlo i předtím - register_shutdown_function()...

ivan_d:

to anode: Díky, sice to není ideál, ale alespoň něco. Ještě si to promyslím, ale asi jsem se stejně s touto vlastností rozloučil.

ikona LLook:

Zapomněl jsi na definici těch výchozích hodnot:

<?php
function set($args){
    $args += array(
        "prvni" => 1,
        "druhy" => 2,
        "treti" => 3,
        "ctvrty" => 4,
        "paty" => 5,
    );
    extract($args);
    doSomething($prvni, $druhy, $treti, $ctvrty, $paty);
}
?>
Zbývá jediný problém k pořešení - jakým způsobem to zapisovat do dokumentačních komentářů?

mj41:

Jo to by byla, protoze pak by bylo o duvod mene tesit se na Perl 6, http://www.perl.com/pub/a/2007/03/01/perl-…-passing.html

ikona error414:

<?php
array_slice
(f(), 1, 5); // php
f()[1:5];               // vetsina ostatnich jazyku

array_item(f(), 5);     // php
f()[5];                 // vetsina ostatnich jazyku
?>

je na posouzeni kazdeho co je prehlednejsi. Dalsi funkce to podle me neresi, tohle by se melo resit na urovni syntaxe jazyka.

Nox:

Zrovna takovéto drobnosti se mi velmi, velmi líbily např. na jazyku Python, kde podobných je spousta ... tak třeba se dočkáme :) no....

ikona MiSHAK:

Pro získání prvního prvku jsem používal <?php
# dříve
$polozka = next(*_fetch_rows($sql));

# nyní díky užasné DiBi
$polozka = $res->fetchSingle();
?>

ikona Jakub Vrána OpenID:

Viz také http://www.error414.com/item/foo_jmeno_trochu_jinak/.

Yossarian:

<?php
$a
= "nejakej.blbej.string";
list(
$b) = split('\\.', $a);
echo
$b;
?>

nejakej

toto nestaci? ;)

Colin:

Ahoj, toto téma při přišlo jako nejvhodnější pro můj dotaz. Chtěl by jsem rozdělit text do poli a nevím, jakou použít správnou funkci, respektive jsem nenašel nic vyhovujícího.
Příklad bude nejjednodušší:
<?php
$text
= 'For Each bunka In Range(Target.Address)';  ?>

Potřebuji rozdělit do pole, aby vypadalo takto:
Array
(
   [0] => 'For'
   [1] => ' '
   [2] => 'Each'
   [3] => ' '
   [4] => 'bunka'
   [5] => ' '
   [6] => 'In'
   [7] => ' '
   [8] => 'Range'
   [9] => '('
   [10] => ' Target'
   [11] => '.'
   [12] => 'Address'
   [13] => ')'
)
Jde tedy i o zachování mezer, závorek a teček. Děkuji moc za nápady.

ikona Jakub Vrána OpenID:

<?php
preg_split
('~(\\W)~', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
?>

Colin:

Děkuji Jakube, moc jsi mi pomohl.

Colin:

Takže nakonec jsem to vyřešil tímto zápisem
<?php
preg_split
('~([()".,\s\n\r])~', $radek, -1, PREG_SPLIT_DELIM_CAPTURE);
?>

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.