This is a discussion on foreach loops are sooooo tricky..... within the PHP Language forums, part of the PHP Programming Forums category; Just something I would like to share: I just learned the hard way (2 days detective work on a bug) ...
|
|||||||
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read |
|
|||
|
Just something I would like to share:
I just learned the hard way (2 days detective work on a bug) that foreach loops are not at all like for loops, not intuitive at all. BEWARE: arrays and matrices are sparse by design/definition in PHP. I'm doing some matrix manipulation in a Finite Element program. Translating Fortran to PHP, because hosters won't allow anything else than PHP. I wish PHP would do array and matrix stuff like Fortran or C, btw. Something for PHP 6 ? Check out this code: $k = array(1=> array(1=>1,1,1,1,1,1), array(2=>1,1,1,1,1), array(3=>1,1,1,1), array(4=>1,1,1), array(5=>1,1), array(6=>1)); /* you would expect this to mirror the matrix about a diagonal from upper left to lower right multiplying each coefficient by 2 on the way... However, the foreach loop is tricky. */ foreach($k as $i=>& $_k) foreach($_k as $j=>&$__k) { $__k *= 2; if ($i != $j) $k[$j][$i ] = $__k; } foreach($k as &$_k) { foreach($_k as &$__k) echo " $__k "; echo "<BR>"; } echo "<BR>"; /* this is better: */ $k = array(1=> array(1=>1,1,1,1,1,1), array(2=>1,1,1,1,1), array(3=>1,1,1,1), array(4=>1,1,1), array(5=>1,1), array(6=>1)); for ($i=1; $i <= 6; $i++) for ($j=$i; $j<= 6; $j++) if (isset($k[$i][$j])) { $k[$i][$j] *= 2 ; if ($i != $j) $k[$j][$i] = $k[$i][$j]; } foreach($k as &$_k) { foreach($_k as &$__k) echo " $__k "; echo "<BR>"; } echo "<BR"; /* * and what about this: * */ $k = array(1=> array(1=>1,1,1,1,1,1), array(2=>1,6=>1), array(3=>1,1,1,1), array(4=>1,1,1), array(5=>1,1), array(6=>1)); for ($i=1; $i <= 6; $i++) for ($j=$i; $j<= 6; $j++) if (isset($k[$i][$j])) { $k[$i][$j] *= 2 ; if ($i != $j) $k[$j][$i] = $k[$i][$j]; } foreach($k as &$_k) { foreach($_k as &$__k) echo " $__k "; echo "<BR>"; } |
|
|||
|
Osiris wrote:
> Just something I would like to share: > > I just learned the hard way (2 days detective work on a bug) that foreach > loops are not at all like for loops, not intuitive at all. BEWARE: arrays > and matrices are sparse by design/definition in PHP. > > I'm doing some matrix manipulation in a Finite Element program. > Translating Fortran to PHP, because hosters won't allow anything else > than PHP. > I wish PHP would do array and matrix stuff like Fortran or C, btw. > Something for PHP 6 ? > Check out this code: > > $k = array(1=> > array(1=>1,1,1,1,1,1), > array(2=>1,1,1,1,1), > array(3=>1,1,1,1), > array(4=>1,1,1), > array(5=>1,1), > array(6=>1)); > > /* > you would expect this to mirror the matrix about a diagonal from upper > left to lower right multiplying each coefficient by 2 on the way... > However, the foreach loop is tricky. */ > foreach($k as $i=>& $_k) > foreach($_k as $j=>&$__k) > { > $__k *= 2; > if ($i != $j) > $k[$j][$i ] = $__k; > } > > foreach($k as &$_k) > { > foreach($_k as &$__k) > echo " $__k "; > echo "<BR>"; > } > > echo "<BR>"; > > /* this is better: */ > > $k = array(1=> > array(1=>1,1,1,1,1,1), > array(2=>1,1,1,1,1), > array(3=>1,1,1,1), > array(4=>1,1,1), > array(5=>1,1), > array(6=>1)); > > for ($i=1; $i <= 6; $i++) > for ($j=$i; $j<= 6; $j++) > if (isset($k[$i][$j])) > { > $k[$i][$j] *= 2 ; > if ($i != $j) > $k[$j][$i] = $k[$i][$j]; > } > foreach($k as &$_k) > { > foreach($_k as &$__k) > echo " $__k "; > echo "<BR>"; > } > echo "<BR"; > /* > * and what about this: > * > */ > $k = array(1=> > array(1=>1,1,1,1,1,1), > array(2=>1,6=>1), > array(3=>1,1,1,1), > array(4=>1,1,1), > array(5=>1,1), > array(6=>1)); > > for ($i=1; $i <= 6; $i++) > for ($j=$i; $j<= 6; $j++) > if (isset($k[$i][$j])) > { > $k[$i][$j] *= 2 ; > if ($i != $j) > $k[$j][$i] = $k[$i][$j]; > } > foreach($k as &$_k) > { > foreach($_k as &$__k) > echo " $__k "; > echo "<BR>"; > } I find foreach loops to be quite intuitive. However, your FORTRAN naming conventions make your code very hard to understand, and I don't have the time to try to figure out what you're trying to do. Try using some descriptive names for your variables. It will make your code a lot easier to understand. -- ================== Remove the "x" from my email address Jerry Stuckle JDS Computer Training Corp. jstucklex@attglobal.net ================== |
|
|||
|
On Dec 1, 6:20 am, Osiris <e...@hotmail.com> wrote:
> > I wish PHP would do array and matrix stuff like Fortran or C, btw. > Something for PHP 6 ? Highly unlikely... Given the PHP team's obsession with OOP, expecting anything quant-friendly is an unrealistic hope. OOP and numerical computing don't mix too well... At the same time, we have to keep in mind that Fortran has been around for 50 years now, so the code base created over all that time is incredibly rich, especially when it comes to numerical stuff. At some point, I needed a couple of statistical routines in PHP, so I ended up porting them from Fortran code written circa 1970... A possible way out would be to take "Numerical Recipes in C" (which is a C clone of "Numerical Recipes in Fortran") and wrap those C functions into a PHP extension, but it still wouldn't solve the problem of availability on shared hosting... Cheers, NC |
|
|||
|
Osiris <et57@hotmail.com> wrote:
.. >Just something I would like to share: > >I just learned the hard way (2 days detective work on a bug) that foreach >loops are not at all like for loops, not intuitive at all. BEWARE: arrays >and matrices are sparse by design/definition in PHP. That's true, but I don't think that's the problem here. >I wish PHP would do array and matrix stuff like Fortran or C, btw. >Something for PHP 6 ? I doubt it. That's simply not its problem domain. >Check out this code: > >$k = array(1=> >array(1=>1,1,1,1,1,1), >array(2=>1,1,1,1,1), >array(3=>1,1,1,1), >array(4=>1,1,1), >array(5=>1,1), >array(6=>1)); > >/* you would expect this to mirror the matrix about a diagonal from upper >left to lower right multiplying each coefficient by 2 on the way... >However, the foreach loop is tricky. */ My guess is that it's not the foreach loop that is biting you, but rather the references. >foreach($k as $i=>& $_k) > foreach($_k as $j=>&$__k) > { > $__k *= 2; > if ($i != $j) > $k[$j][$i ] = $__k; > } What does this produce for you? Does the next-to-the-last line really do what you think? Is that making a copy of the value, or is it storing a reference to the value? That is, won't $k[2][1] simply be a reference to $k[1][2], and not a separate value? My only convenient server runs PHP 4, which does not support the reference notation. If I change your code to this, which I believe to be equivalent: foreach($k as $i=> $_k) foreach($_k as $j=>$__k) { $__k *= 2; $k[$i][$j] = $__k; if ($i != $j) $k[$j][$i ] = $__k; } the result is a 6x6 square array where every element is 2. -- Tim Roberts, timr@probo.com Providenza & Boekelheide, Inc. |
|
|||
|
On Sat, 01 Dec 2007 09:36:47 -0500, Jerry Stuckle wrote:
> Osiris wrote: >> Just something I would like to share: >> >> I just learned the hard way (2 days detective work on a bug) that foreach >> loops are not at all like for loops, not intuitive at all. BEWARE: arrays >> and matrices are sparse by design/definition in PHP. >> >> I'm doing some matrix manipulation in a Finite Element program. >> Translating Fortran to PHP, because hosters won't allow anything else >> than PHP. >> I wish PHP would do array and matrix stuff like Fortran or C, btw. >> Something for PHP 6 ? >> Check out this code: >> >> $k = array(1=> >> array(1=>1,1,1,1,1,1), >> array(2=>1,1,1,1,1), >> array(3=>1,1,1,1), >> array(4=>1,1,1), >> array(5=>1,1), >> array(6=>1)); >> >> /* > > I find foreach loops to be quite intuitive. However, your FORTRAN > naming conventions make your code very hard to understand, and I don't > have the time to try to figure out what you're trying to do. > > Try using some descriptive names for your variables. It will make your > code a lot easier to understand. > There is nothing to describe , really... this example is just about a general matrix with numbers to be mirrored. The issue here is, that in a foreach loop you can inadvertedly enter extra coefficients in the matrix, that are processed in a next iteration. A for loop determins exactly which coefficients to process. A foreach loop processes all that it encounters, new or old. Mind you: I know what the problem is with the code. I did not say PHP was in error, just that foreach-es are not alway what you EXPECT them to be. |
|
|||
|
On Sun, 02 Dec 2007 00:53:06 +0000, Tim Roberts wrote:
> Osiris <et57@hotmail.com> wrote: > . >>Just something I would like to share: >> >>I just learned the hard way (2 days detective work on a bug) that foreach >>loops are not at all like for loops, not intuitive at all. BEWARE: arrays >>and matrices are sparse by design/definition in PHP. > > That's true, but I don't think that's the problem here. > >>I wish PHP would do array and matrix stuff like Fortran or C, btw. >>Something for PHP 6 ? > > I doubt it. That's simply not its problem domain. > >>Check out this code: >> >>$k = array(1=> >>array(1=>1,1,1,1,1,1), >>array(2=>1,1,1,1,1), >>array(3=>1,1,1,1), >>array(4=>1,1,1), >>array(5=>1,1), >>array(6=>1)); >> >>/* you would expect this to mirror the matrix about a diagonal from upper >>left to lower right multiplying each coefficient by 2 on the way... >>However, the foreach loop is tricky. */ > > My guess is that it's not the foreach loop that is biting you, but rather > the references. > >>foreach($k as $i=>& $_k) >> foreach($_k as $j=>&$__k) >> { >> $__k *= 2; >> if ($i != $j) >> $k[$j][$i ] = $__k; >> } > > What does this produce for you? Does the next-to-the-last line really do > what you think? Is that making a copy of the value, or is it storing a > reference to the value? That is, won't $k[2][1] simply be a reference to > $k[1][2], and not a separate value? > this works ok, so the referencing is not the problem: <?php $f = array(1=>array(5=>1,3=>2,8=>3),array(3=>2,4,5)); foreach($f as &$_f) foreach($_f as &$__f) echo $__f." "; for ($i=1; $i <= 6; $i++) { for ($j=1; $j <= 6; $j++) echo $k[$i][$j]." "; echo "<BR>"; } ?> The thing is, that while mirroring a coefficient, it may be processed again in a next iteration. At the start of the routine, there is an upper triangular matrix, but next, some elements of the lower triangle are added, having BEEN doubled. foreach apparently does a complete search for new elements each loop. Correct , but it's a very different mechanism than a for loop, which pinpoints each element to process Now for the even more weird thing: running the above loops in eclipse debugger, I get this: 2 4 4 4 4 4 4 2 4 4 4 4 4 4 2 4 4 4 4 4 4 2 4 4 4 4 4 4 2 4 4 4 4 4 4 2 Running it in Firefox on my localhost (apache 2), i get this: 2 2 2 2 2 2 2 1 1 1 1 1 2 2 1 1 1 1 2 2 2 1 1 1 2 2 2 2 1 1 2 2 2 2 2 1 |
|
|||
|
The issue is, that foreach loop process any element of a matrix that it
will encounter. For loops only the indexed elements (i,j) When mirroring the upper triangular matrix, I ADD elements. But now for the really kinky thing: running the following routine on my localhost(apache 2.2.3,PHP5), I get 2 2 2 2 2 2 2 1 1 1 1 1 2 2 1 1 1 1 2 2 2 1 1 1 2 2 2 2 1 1 2 2 2 2 2 1 Running it through the Eclipse PHP Zend debugger I get (which, in my opinion, is right): 2 4 4 4 4 4 4 2 4 4 4 4 4 4 2 4 4 4 4 4 4 2 4 4 4 4 4 4 2 4 4 4 4 4 4 2 <?php $k = array(1=> array(1=>1,1,1,1,1,1), array(2=>1,1,1,1,1), array(3=>1,1,1,1), array(4=>1,1,1), array(5=>1,1), array(6=>1)); foreach($k as $i=>& $_k) foreach($_k as $j=>&$__k) { $__k *= 2; if ($i != $j) $k[$j][$i ] = $__k; } for ($i=1; $i <= 6; $i++) { for ($j=1; $j <= 6; $j++) echo $k[$i][$j]." "; echo "<BR>"; } ?> I make this a new thread, to get explanations. |
|
|||
|
Osiris wrote:
> On Sat, 01 Dec 2007 09:36:47 -0500, Jerry Stuckle wrote: > >> Osiris wrote: >>> Just something I would like to share: >>> >>> I just learned the hard way (2 days detective work on a bug) that foreach >>> loops are not at all like for loops, not intuitive at all. BEWARE: arrays >>> and matrices are sparse by design/definition in PHP. >>> >>> I'm doing some matrix manipulation in a Finite Element program. >>> Translating Fortran to PHP, because hosters won't allow anything else >>> than PHP. >>> I wish PHP would do array and matrix stuff like Fortran or C, btw. >>> Something for PHP 6 ? >>> Check out this code: >>> >>> $k = array(1=> >>> array(1=>1,1,1,1,1,1), >>> array(2=>1,1,1,1,1), >>> array(3=>1,1,1,1), >>> array(4=>1,1,1), >>> array(5=>1,1), >>> array(6=>1)); >>> >>> /* > >> I find foreach loops to be quite intuitive. However, your FORTRAN >> naming conventions make your code very hard to understand, and I don't >> have the time to try to figure out what you're trying to do. >> >> Try using some descriptive names for your variables. It will make your >> code a lot easier to understand. >> > > There is nothing to describe , really... this example is just about a > general matrix with numbers to be mirrored. > > The issue here is, that in a foreach loop you can inadvertedly enter extra > coefficients in the matrix, that are processed in a next iteration. > A for loop determins exactly which coefficients to process. A foreach loop > processes all that it encounters, new or old. > Mind you: I know what the problem is with the code. I did not say PHP was > in error, just that foreach-es are not alway what you EXPECT them to > be. Well, a foreach loop is supposed to go through all of the elements. So I would expect it to also process additional elements if you add them in the loop. However, if you add extra elements to an array, the for loop will also process them, i.e. $myArray[0] = 0; for ($i=0; $i<count($myArray); $i++) { $myArray[$i+1] = $i*2+2; } will run for a long time... -- ================== Remove the "x" from my email address Jerry Stuckle JDS Computer Training Corp. jstucklex@attglobal.net ================== |