I have this snippet of code and it's working just fine (because in my example that variable really exists in $arrival_time so it breaks at about $k = 10).
$arrival_time = explode(",", $arrival_timeAll[1]);
$sizeOfArrival = sizeof($arrival_time);
$k = -1;
while (++$k < $sizeOfArrival) {
if ($arrival_time[$k] >= $someVariable) {
break;
}
}
Isn't that the same as this code? I added the while(true) loop and increased the "break level" - so it's now break 2, not just break. But seems that's an infinite loop. Why?
while (true) {
$arrival_time = explode(",", $arrival_timeAll[1]);
$sizeOfArrival = sizeof($arrival_time);
$k = -1;
while (++$k < $sizeOfArrival) {
if ($arrival_time[$k] >= $someVariable) {
break 2;
}
}
}
Why adding while(true)? Because I need to define some more statements (which here aren't neccesary for explanation) if inside while loop doesn't find the matching one (if that "break" in first case, "break 2" in second case doesn't run).
Anyway - why this isn't working?
In some cases, the predicate for the inner while loop is never hit, thus the while loop is never executed. As you don't have a break after it, the loop will run forever. This case would be if the array is empty, 0 < 0 is false.
Starting with $k = -1, the first time the predicate gets evaluated, $k will be 0 as you are using the preincrement operator, for an empty array this will evaluate to false and the code with run infinite. Without the while(true) loop this wouldn't cause any problems as you'd just jump straight over it.
Related
I've stumbled upon a syntax in a code example I have never seen in for-loops before in PHP.
What does this do? WHY should I use this?
for(;$i<$max;){
$i++;
//code..
}
I could figure out that it was not the same as
for($i=0;$i<$max;$i++) {
//code...
}
I don't understand the difference between the two examples above.
If for being more specific about my thoughts.
If we have this code (taken from a solution from adventofcode):https://www.reddit.com/r/adventofcode/comments/kdf85p/2020_day_15_solutions/
<?php
$cap = 2020;
$bits = [5,1,9,18,13,8,0];
$i=0;
$time = [];
foreach($bits as $bit) {
$i++;
$time[$bit] = $i;
$say = 0;
}
for(;$i<$cap-1;){
$i++;
if(isset($time[$say])) {
$last = $time[$say];
}
else {
$last = $i;
}
$time[$say] = $i;
$say = $i - $last;
}
echo $say;
?>
and compare that to:
<?php
$cap = 2020;
$bits = [5,1,9,18,13,8,0];
$i=0;
$time = [];
foreach($bits as $bit) {
$i++;
$time[$bit] = $i;
$say = 0;
}
for($i=0;$i<$cap-1;$i++){
if(isset($time[$say])) {
$last = $time[$say];
}
else {
$last = $i;
}
$time[$say] = $i;
$say = $i - $last;
}
echo $say;
?>
I get different results in $say.(376 in first example and 38 in last example).
Why do I get different values?
Well based on the PHP docs:
https://www.php.net/manual/en/control-structures.for.php
for loops are the most complex loops in PHP. They behave like their C
counterparts. The syntax of a for loop is:
for (expr1; expr2; expr3)
statement
While:
Each of the expressions can be empty or contain multiple expressions
separated by commas.
So it's just a way to "save code" when your first expression of the for-loop is "obvious", So instead of mentioning the default obvious expression - you just "skip" it.
According to your example:
for(;$i<$cap-1;)
we skipped the first expression (expr1) as $i has been already defined as 0 ($i = 0;) earlier in the code block so it's "skippable". Doesn't affect the code.
But, The third expression:
At the end of each iteration, expr3 is evaluated (executed).
Since we don't mention it in the loop, we are responsible to handle the "increasement" (in this case) of $i in the loop itself.
However - the main difference in the code is that if you mention the third expression - it's evaluated at the end of each iteration but in the code block (your example) - we increase the $i variable at the beginning of the code.
The main difference between the two blocks (assuming $i is initialised to 0 before the for(;$i<$max;){ loop) is that the first loop increments $i before running the loop code, where the second loop increments $i after running the loop code. As a trivial example:
function code($i) {
echo "$i\n";
}
$max = 3;
$i = 0;
for(;$i<$max;){
$i++;
code($i);
}
for($i=0;$i<$max;$i++) {
code($i);
}
The output of the first loop is:
1
2
3
while the output of the second loop is:
0
1
2
Note (as pointed out by #IMSoP), the condition clause is executed at the beginning of the loop, and can have side-effects, so you could also emulate the first loop with this code:
for($i=0;$i++<$max;) {
code($i);
}
C-style for loops are a rather peculiar piece of syntax. They actually consist of three expressions, any of which can do anything you like, or even be empty:
An expression to execute for its side effects once before the loop begins
An expression to evaluate at the beginning of each iteration, to see if the loop should terminate
An expression to execute for its side effects at the end of each iteration
The most common way to use them is:
Initialise a counter of some sort
Check if the counter has reached some value
Increment or decrement the counter
But those aren't built into the language at all, and leaving one of the expressions out doesn't apply any default behaviour, it just does nothing - except that leaving the second expression empty always evaluates to true.
So, for instance:
for(;true;) is an infinite loop: it does nothing, checks true, and does nothing again
for(;;) is the same infinite loop, because the empty expression in the middle is considered true
for($i=0;;) is just the same infinite loop, but with $i initialised to 0 before running it
for(;true;foo()) is the same loop, but with the function foo() run at the end of every iteration
In your case, the loop is for(;$i<$max;) which breaks down to:
Before starting the loop, do nothing
At the beginning of each iteration, terminate if the expression $i<$max happens to be false
At the end of each iteration, do nothing
It doesn't do anything to control what values $i and $max have before or during the loop.
In fact, it's just the same as while($i<$max) and would probably be much clearer if written that way.
The example shown seems to have ended up that way because the author wanted to move the $i++ to the beginning of each iteration, rather than the end (although why they also left out the $i=0 I'm not sure). But the for syntax supports that too! You can actually include multiple expressions with a comma between in any of the three positions; if they're in the second position, the last one is what determines if the loop exits.
So you can do this:
for($i=0; $i++,$i<=$max; )
Now, $i will be incremented at the beginning of the loop; since it will be incremented before the test, you have to also adjust the condition from < to <= to make sure it runs for one more iteration.
For that particular case, there's another way as well: incrementing a variable returns a value. If you write ++$i it returns the value after incrementing, and if you write $i++ it returns the value before incrementing. So both of these would also work:
for($i=0; ++$i <= 10; ) echo $i, PHP_EOL;
for($i=0; $i++ < 10; ) echo $i, PHP_EOL;
In your first example, $i is registered somewhere before. so $i can be 0 or other zero, because if you use code like
for(;$i<$max;){
$i++;
//code..
}
you got error Undefined variable $i in and later $i is registered.
/* example 1 */
for ($i = 1; $i <= 10; $i++) {
echo $i;
}
/* example 2 */
for ($i = 1; ; $i++) {
if ($i > 10) {
break;
}
echo $i;
}
/* example 3 */
$i = 1;
for (; ; ) {
if ($i > 10) {
break;
}
echo $i;
$i++;
}
https://www.php.net/manual/en/control-structures.for.php
what is your point?
I currently have a while loop which has another while loop running inside of it, I want to reference a value which is changed by the first loop each time the second loop is run.
$counter = 0;
$counter2 = 2;
while ($counter < 7) {
$name = 0;
while ($array[$counter2][2] == !false) {
if ($array[$counter2][3] == "string") {
if ($array[$counter2][2] == $counter) {
$name = 1;
}
}
$counter2++;
}
$counter++;
}
When I run the above and do the second IF statement the value of $counter is 0 even though this should go up through each iteration of the first loop.
How do I get the value of $counter in the second while loop to match the $counter I am using in the first while loop?
The second loop was only ever running once because the array search would always start off as false due to the fact I wasn't resetting the second counter each time the first loop was run.
I know one should not modify physical structure of array while looping by reference, but I need explanation of what is going on in my code. Here we go:
$x= [[0],[1],[2],[3],[4]];
foreach ($x as $i => &$upper) {
print $i;
foreach ($x as $j => &$lower) {
if($i == 0 && $j == 2) {
unset($x[2]);
} else if($i == 1 && $j == 3) {
unset($x[3]);
}
}
}
The output is 01. Surprising that outer loop iterates only twice, for indices 0 and 1. I was expecting the output to be 014.
I have read lots of blog posts and questions about hazards of using array references, but nothing that can explain this phenomenon. I am breaking my head over it for hours now.
EDIT:
The code above is the minimal reproducible code. One explanation (but an incorrect one) that might seem to be the case is this:
The outer loop goes through two iterations before the internal pointer is set to index 2. But the loop does not find any element at index 2 and thus thinks no elements are left and quits.
The problem with this theory is it doesn't quite explain this code:
$x= [[0],[1],[2],[3],[4]];
foreach ($x as $i => &$upper) {
print $i;
foreach ($x as $j => &$lower) {
if($i == 0 && $j == 2) {
unset($x[2]);
// No if else here
unset($x[3]);
}
}
}
By the same token, the above code should also produce 01, but its actual output is 014, as expected. Even when two items in a series are removed, php knows that are still elements left to be iterated over. Could this possibly be a bug with php scripting engine?
A simple code to reproduce your issue:
$x = [0, 1, 2];
foreach ($x as $k => &$v) {
print $k;
if ($k == 0) {
unset($x[1]);
}
end($x); // move IAP to end
next($x); // move IAP past end (that's the same as foreach ($x as $y) {} would do)
}
If you foreach over an array, it's copied (= no problem when iterating, you'll iterate over the full original array).
But if you foreach by reference, the array is not copied (the reference needs to match the original array, so copying impossible).
Foreach internally always saves the position of the next element to iterate over.
But when the next position of an array is removed, foreach needs to go back to the array and check it's internal array pointer (IAP).
In this case the next position is destroyed and the IAP is past the end, it ends the loop.
That's what you're seeing here.
Also interesting: hhvm has a different behaviour to php here: http://3v4l.org/81rl8
Addendum: The infinite foreach loop:
$x = [0,1,2];
foreach ($x as $k => &$v) {
print $k;
if ($k == 1) {
unset($x[2]);
} else {
$x[2] = 1;
}
reset($x);
}
If you understood my explanations above, guess why that loops indefinitely.
Is there a way to continue on external foreach in case that the internal foreach meet some statement ?
In example
foreach($c as $v)
{
foreach($v as $j)
{
if($j = 1)
{
continue; // But not the internal foreach. the external;
}
}
}
Try this, should work:
continue 2;
From the PHP Manual:
Continue accepts an optional numeric argument which tells it how many levels of enclosing loops it should skip to the end of.
here in the examples (2nd exactly) described code you need
Try this: continue 2; According to manual:
continue accepts an optional numeric argument which tells it how many levels of enclosing loops it should skip to the end of.
There are two solutions available for this situation, either use break or continue 2. Note that when using break to break out of the internal loop any code after the inner loop will still be executed.
foreach($c as $v)
{
foreach($v as $j)
{
if($j = 1)
{
break;
}
}
echo "This line will be printed";
}
The other solution is to use continue followed with how many levels back to continue from.
foreach($c as $v)
{
foreach($v as $j)
{
if($j = 1)
{
continue 2;
}
}
// This code will not be reached.
}
This will continue to levels above (so the outer foreach)
continue 2
<?php
foreach($c as $v)
{
foreach($v as $j)
{
if($j = 1)
{
continue 2; // note the number 2
}
}
}
?>
RTM
Try break instead of continue.
You can follow break with an integer, giving the number of loops to break out of.
you have to use break instead of continue, if I get you right
Here I wrote an explanation on the matter: What is meant by a number after "break" or "continue" in PHP?
Simple question:
Why is this:
for($k=1;$k<=10;$k+2) { }
giving an infinite loop? When I change $k+2 by $k++, it works fine.
How can I correct it? (I can't change the 10 for an impair number because I need this function to work either with a pair or impair value at that place)
$k+2
This won't change $k's value, so it never get's higher than 10. Probably you meant:
$k+=2
Which will increase $k by two each time the expression get's evaluated (at the end of each for-loop).
for($k=1;$k<=10; $k = $k+2) { }
or
for($k=1;$k<=10; $k += 2) { }
it is infinite loop because $k is not updated, try this instead
for($k = 1; $k <= 10; $k = $k + 2) {}
or
for($k = 1; $k <= 10; $k += 2) {}
Reference: PHP operators