[PHP]Dziwne zachowanie array_walk_recursive |
[PHP]Dziwne zachowanie array_walk_recursive |
27.02.2014, 23:38:05
Post
#1
|
|
Grupa: Zarejestrowani Postów: 292 Pomógł: 89 Dołączył: 27.12.2006 Skąd: Warszawa Ostrzeżenie: (0%) |
Witam.
Natrafiłem dziś na dość dziwne zachowanie array_walk_recursive, mam nadzieję że ktoś mądrzejszy mnie oświeci co jest tu grane Generalnie chciałem napisać funkcję sumującą wszystkie elementy wielowymiarowej tablicy. Mam np. taką tablicę:
I początkowo napisałem takie coś:
Rezultat okazał się, delikatnie mówiąc, zaskakujący:
Z powyższego wynika, że array_walk_recursive przy napotkaniu kolejnej tablicy przekazuje wcześniej obliczony parametr $sum dalej co jest jak najbardziej właściwym zachowaniem. Problem w tym, że jak "wychodzi" z danej tablicy i "wchodzi" do kolejnej na tym samym poziomie zagnieżdżenia, to przekazuje ten sam $sum, co do pierwszej tablicy. Ciężko to opisać, ale jak się przyjrzycie to zrozumiecie o co mi chodzi Tak jakby na każdym poziomie zagnieżdżenia była jedna lokalna wartość $sum obliczona na podstawie wcześniejszych elementów. Najlepiej widać to jak się popatrzy na pierszą, drugą, trzecią i ostatnią liczbę z przykładowego rezultatu (nie biorąc pod uwagę "wynik:0" - o tym zaraz ). Czyli mamy "1" (pierwszy element tablicy), potem jest "3" (1 + 2), potem "22" (1 + 2 + 19), natomiast na końcu jest "23", bo ostatnim elementem tablicy jest "1" (czyli 1 + 2 + 19 + 1 = 23). Czy ja czegoś nie rozumiem? Czy to jest bug? Druga sprawa to: "wynik:0". No właśnie, dlaczego? Przecież parametr $sum przekazywany jest do funkcji przez referencję, więc teoretycznie obie zmienne odwołują się do tej samej zmiennej. Poza tym widać wyraźnie, że parametr $sum jest modyfikowany podczas wykonywania array_walk_recursive, a mimo to na końcu ma on nadal wartość "0". Generalnie problem rozwiązałem w ten sposób:
Jednak interesuje mnie to arcydziwne zachowanie funkcji w pierwszym przykładzie. Czy faktycznie jest tak późno, że nie widzę czegoś oczywistego? -------------------- Zend Certified Engineer | Microsoft Certified Professional: Programming in HTML5 with JavaScript & CSS3 | Blog
|
|
|
28.02.2014, 05:51:25
Post
#2
|
|
Grupa: Zarejestrowani Postów: 279 Pomógł: 60 Dołączył: 25.02.2012 Ostrzeżenie: (0%) |
Dokładnie tak jak zaobserwowałeś, chodzi o to, że array_walk_recursive jest jakby uruchamiane od nowa na każdy nowy poziom zagłębienia tablicy, od nowa wywołuje iterację po aktualnym poziomie z zdefiniowaną funkcją oraz aktualną, istniejącą w danym zasięgu widoczności wartością $sum. Na każdy taki nowy poziom iteracji brana jest aktualna wartość $sum i zostaje użyta przy iteracji, a dopiero w danej iteracji (na jednym poziomie zagłębienia) pomiędzy kolejnymi wywołaniami funkcja anonimowa otrzymuje referencję do $sum.
Widać to wyraźnie przy takim zapisie:
Niestety, zapis:
jest niemożliwy (powoduje błąd). Stąd wywołanie: daje takie dziwne wyniki i końcowa wartość $sum = 0 (bo w początkowym zasięgu widoczności wartość $sum się nie zmieniła - tylko wartość $sum jest przekazywana do iteracji, nie referencja). No a ostatni przykład robi co chcesz, bo właśnie do tego jest to use(&$sum) - aby można użyć referencji. Dobrze jego zachowanie widać po tym:
Swoją drogą, tak samo działa:
-------------------- there is much to be learned
|
|
|
28.02.2014, 11:30:03
Post
#3
|
|
Grupa: Zarejestrowani Postów: 292 Pomógł: 89 Dołączył: 27.12.2006 Skąd: Warszawa Ostrzeżenie: (0%) |
Cytat array_walk_recursive jest jakby uruchamiane od nowa na każdy nowy poziom zagłębienia tablicy, od nowa wywołuje iterację po aktualnym poziomie z zdefiniowaną funkcją oraz aktualną, istniejącą w danym zasięgu widoczności wartością $sum Właśnie to jest dość dziwne dla mnie. Tzn. nie wiem w czym to w sumie pomaga, jak dla mnie bardziej logiczne byłoby przekazywanie referencji do zmiennej przy każdej iteracji, a nie przy zmianie poziomów zagnieżdżenia. Generalnie dotychczas myślałem o array_walk_recursive (i o innych tego typu funkcjach), że, najprościej mówiąc, spłaszcza ona tymczasowo tablicę po której iteruje. Wychodzi na to, że jest inaczej. Ogólnie mówiąc, dla mnie to działanie jest lekko "buggy" Cytat o a ostatni przykład robi co chcesz, bo właśnie do tego jest to use(&$sum) - aby można użyć referencji Tutaj też nie dokońca rozumiem, dlaczego w funkcji anonimowej nie można po prostu użyć referencji. Chociaż z drugiej strony "może" to rozumiem. Generalnie na początku kierowałem się tym, że np. w funkcji preg_match() podajesz parametr, powiedzmy $matches, który będzie zawierał informację o poszczególnych dopasowaniach. I to działa. Z tym że teraz właśnie do mnie doszło, że przecież to jest tak naprawdę zwykła funkcja biorąca jeden z parametrów przez referencję, podczas gdy w przypadku array_walk_recursive w całym procesie "pośredniczy" jeszcze callback. I pewnie właśnie dlatego jest tak jak mówisz - po to wymyślono użycie "use". Właściwie jak to napisałem teraz, to to zrozumiałem Wniosek: "there is much to be learned" - masz rację -------------------- Zend Certified Engineer | Microsoft Certified Professional: Programming in HTML5 with JavaScript & CSS3 | Blog
|
|
|
Wersja Lo-Fi | Aktualny czas: 23.04.2024 - 11:40 |