Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Eval, czy rekurencja?
Forum PHP.pl > Forum > PHP
Vengeance
Witam,

Powiedzmy że mamy tablicę do której kluczy zapisujemy liczbę. Problem w tym, że nie wiadomo z góry ile tych kluczy może być po drodze do wartości.
Czyli:
  1. <?php
  2. $arr['test'][''] = 4;
  3. $arr['test']['lorem'] = 6;
  4. $arr['abc']['xyz']['abecadlo']['php'] = 8;
  5.  
  6. // chce pobrac wartosc 8, więc:
  7. $path = array('abc', 'xyz', 'abecadlo', 'php');
  8.  
  9. // zwraca wartosc (docelowo referencje na wartosc)
  10. function pobierzWartosc($path) {
  11.  // ...
  12.  return $arr[...][...][...][...];
  13. }
  14. ?>


Widzę dwa sposoby w jakie można to rozwiązać:
- zastosowanie rekurencji
- wygenerowanie przy pomocy pętli for/foreach stringu ($arr[...][...][...][...]), który potem odpalimy przez eval()
- a może jest jakiś trzeci?

I teraz się zastanawiam która z tych metod okaże się najlepsza pod względem wydajności zarówno dla np. 3 zagnieżdzeń jak i 300.
Zawsze odradzałem używanie eval() ale rekurencja też tutaj może położyć wszystko na łopatki chyba.

---
zaraz będę testował to jakimiś microtime(), ale piszę post bo może jest rozwiązanie inne o którym nie pomyślałem lub ktoś miał już podobny problem.

Na razie mam tak:

  1. <?php
  2. function recursive_getValue($arr, $path) {
  3.    if(!is_array($v=$arr[$path[0]]))
  4.        return $v;
  5.    else {
  6.        $key = array_shift($path);
  7.        return recursive_getValue($arr[$key], $path);
  8.    }
  9. }
  10.  
  11. function eval_getValue($id, $warianty) {
  12.    global $arr;
  13.    
  14.    $str = '$arr'."[$id]['".join("']['", $warianty)."']";
  15.    return eval("return $str;");
  16. }
  17.  
  18. recursive_getValue($arr, array(4, 'S', 'zielony', 'haft'));
  19. eval_getValue(4, array('S', 'zielony', 'haft'));
  20. ?>

Kod
Recursive: 1.83044290543 seconds
Eval: 1.78684926033 seconds
Total Elapsed: 3.61737394333 seconds
Recursive: 51 %
Eval: 49 %

Oba rozwiązania idą łeb w łeb 50:50 przy 100000 powtórzeń i szukaniu na 4 poziomie tabeli.

Kod
Recursive: 95.8138742447 seconds
Eval: 13.8335242271 seconds
Total Elapsed: 109.64747715 seconds
Recursive: 87 %
Eval: 13 %

No i mamy przewagę oczywiście eval przy 100000 powtózeń i szukaniu na jakimś 70 poziomie tabeli.

Tylko czy niebezpieczeństwo użycia eval jest warte poprawie wydajności?
W moim przypadku nie, bo szukanie będzie odbywało się gdzieś tak maksymalnie do 5 poziomów, a kod funkcji
rekurencyjnej jest bardziej niezawodny.

Ktoś ma jakieś sugestie? Pzdr
dr_bonzo
Zamiast rekursji sproboj zwykla petla:

$array = $array[$nextKey]...
Kocurro
  1. <?php
  2.  
  3. function pobierzWartosc( &$aValues, $aPath )
  4. {
  5.  $aTemp = $aValues;
  6.  $aTempPath = $aPath;
  7.  
  8.  while ( count( $aTempPath ) > 0 && is_array( $aTemp ) )
  9. {
  10.   foreach ( $aTemp as $sKey => $mValue )
  11.   {
  12.      if ( $sKey == $aTempPath[0] )
  13.      {
  14.         array_shift( $aTempPath );
  15.         $aTemp = $aTemp[$sKey]; // nie wiem czy nie powinno być $aTemp = &$aTemp[$sKey];
  16.         if ( count( $aTempPath ) == 0 )
  17.         {
  18.            return $mValue;
  19.            continue 2;
  20.         }
  21.      }
  22.   }
  23. }
  24.  
  25. return null;
  26. }
  27. ?>


Coś w tym stylu - nie mam czasu by przetestować więc mogą być błędy.

Pozdrawiam,
Łukasz


----

ps: Dobrze płacicie w MIMO ? bo nie wiem czy opłaca mi się pisać CV
Vengeance
Kod
$aTemp = $aTemp[$sKey]; // nie wiem czy nie powinno być $aTemp = &$aTemp[$sKey];


Dodanie & zwiększa czas wykonywania.

Ale ogólnie twój sposób działa lepiej niż Recursive, gorzej niż eval:
Recursive: 65 %
Eval: 8 %
Wszystko w jednej funkcji: 27 %
Kocurro
Jeśli chodzi o eval to będzie on szybszy jeśli wykonasz go wielokrotnie z tym samym kodem - silnik PHP tak działa. Przy różnym kodzie może być już słabszy wynik. Użycie akceleratora w postaci eAccelerator bądź xCache przyśpieszy jeszcze bardziej.

Pozdrawiam,
Łukasz
Vengeance
Tak domyślam się. Mimo wszystko użycie eval jakoś nie leży w mojej naturze winksmiley.jpg
W momencie gdy napisałeś posta właśnie robiłem 3 wersje funkcji, podobną do Twojej - i to się wydaje rozwiązaniem najbardziej zadowalającym
Kocurro
Tylko przetestuj czy dobrze z referencjami sobie radzi - bo chciałeś by nie wartość a nie referencje zwracało
Vengeance
Wersja zwracająca referencje na wartość. Zmian w lini 6, 11 i 16
  1. <?php
  2.  
  3. function pobierzWartosc( &$aValues, $aPath )
  4. {
  5. $aTemp =& $aValues;
  6. $aTempPath = $aPath;
  7.  
  8. while ( count( $aTempPath ) > 0 && is_array( $aTemp ) )
  9. {
  10.  // edit
  11.  if(!array_key_exists($aTempPath[0], $aTemp))
  12.     $aTemp[$aTempPath[0]] = array();
  13.  
  14.  foreach ( $aTemp as $sKey => &$mValue )
  15.  {
  16.     if ( $sKey == $aTempPath[0] )
  17.     {
  18.        array_shift( $aTempPath );
  19.        $aTemp =& $aTemp[$sKey]; // nie wiem czy nie powinno być $aTemp = &$aTemp[$sKey];
  20.        if ( count( $aTempPath ) == 0 )
  21.        {
  22.           return $mValue;
  23.           continue 2;
  24.        }
  25.     }
  26.  }
  27. }
  28.  
  29. return null;
  30. }
  31.  
  32. // czyli
  33. $aValues = array(
  34. ...
  35. );
  36. $t =& pobierzWartosc( $aValues, $aPath );
  37. $t = 3;
  38. $t =& pobierzWartosc( $aValues, $aPath );
  39. echo $t;// == 3;
  40. ?>


---
dopisałem jeszcze linie 13, 14 gdyż chciałem aby funkcja zwracała również referencje na coś co nie istnieje.
Teraz moge zrobic powiedzmy
Kod
if(!is_int($t =& pobierzWartosc( $aValues, array('jakas', 'sciezka', 'dluga') );
  $t = 0;
else
  $t++;
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.