difference between continue 2 and break in nested loops - php

I was refactoring some old code and stumbled on a continue 2 that can be easily replaced with a break.
for($rows as $i){
for($columns as $j){
if( Helper::unicornExists($i, $j) ){
//continue 2;
break;
}
}
}
If we say that continue 2 makes the code more complicated and hard to read ,
is there any good reason to use it (in 2 level) nested loops ?

In this particular example it seems that it's the same thing and it is up to you to decide how you prefer it. One reason I can see to keep continue 2 would be if in a future development of your project you would add something after the inner for
for($rows as $i){
for($columns as $j){
if( Helper::unicornExists($i, $j) ){
//continue 2;
break;
}
}
echo 'done with ', $i, PHP_EOL;
}
You need to think what you expect if the unicorn does exist. Do you want to skip just the inner loop, and that's what break would do, or you want to skip the outer one also, and that's what continue 2 would do.

Related

Break out of unknown number of nested loops without using go to?

I am writing some code which will frequently contain loops that I need to break out of under certain conditions. The problem is that sometimes, I am nested 3 levels deep, some times 4 and some times 5 or more.
I could break out by writing break 3, break 4 and break 5 etc. but that requires me to keep track of the current depth inside each nested loop. Is there an easier way of breaking out of unknown number of nested loops without using goto?
I think the question needs a little more explanation.
There are 4 nested for loops in part of a code.
Somewhere down the line there are 3 nested for loops.
Then somewhere else there are 5 nested for loops.
There are all independent of each other and I only have to go through one of them for each run of the code. The one I loop through depends on value of certain parameters.
I could break out of them by keeping track of for loops in each case and using break number but doing it for 100-200 different sets of nested for loops is tiring. It will also not work if the number of nested loops have to be updated.
I was hoping that there is some code in PHP which could just break out of all loops at once without me keeping track.
If you know the depth of the loop you want to break out of, but not how many levels down you are from that, you can use a variable that each loop checks.
$break_level = 99;
while (...) {
while (...) {
while (...) {
while (...) {
...
if (...) {
$break_level = 2;
break;
}
...
}
if ($break_level <= 3) {
break;
}
}
if ($break_level <= 2) {
break;
}
}
if ($break_level <= 1) {
break;
}
}
But this kind of generality is hardly ever needed. I've written millions of loops in my lifetime, and I hardly ever need to break out of anything other than the current loop, its immediate container, or the entire set of nested loops. In these cases there's often a variable that can be checked. For instance, if you're searching a multidimensional array, just set a $found variable:
$found = false;
foreach ($array as $level1) {
foreach ($level1 as $level2) {
if (...) {
$found = true;
break;
}
}
if ($found) {
break;
}
}

Continue to the next loop in a foreachloop with another nested foreachloop

I have 2 Foreach-Loops. One of them is nested inside the other, e.g.:
foreach(...){
foreach(...){
//if statement
}
}
Within the inner loop I got an if statement and if this statement returns true I want to break the inner loop and continue with the next outter loop. Is there any way to realize this? "break 2; " won't work as I need to continue with the outter loop.
Like with break, you can add a number to continue as well:
foreach(...) {
foreach(...) {
if (...)
continue 2;
}
// this will be skipped
}
From the docs:
continue accepts an optional numeric argument which tells it how many
levels of enclosing loops it should skip to the end of. The default
value is 1, thus skipping to the end of the current loop.
Per PHP documentation, the default is 1, which only breaks out of the immediately encapsulating control struct
break ends execution of the current for, foreach, while, do-while or
switch structure.
break accepts an optional numeric argument which tells it how many
nested enclosing structures are to be broken out of. The default value
is 1, only the immediate enclosing structure is broken out of.
For example, the below code:
<?php
foreach(array(1,2,3,4,5) as $num){
foreach(array('a', 'b') as $char){
if($char === 'b') {
break;
}
echo $char;
}
echo $num;
}
// Result: a1a2a3a4a5
It breaks before it prints 'b', but continues with the loop to 5.

for loop not processing first element of array [duplicate]

This question already has answers here:
The 3 different equals
(5 answers)
Closed 6 years ago.
I am puzzled by an issue in my code and hoping someone else can help me shed some light on why my loop is omitting the first element (array[0]) of the array.
the Code
foreach ($a as $key => $val) {
for ($i=0; $i<count($val); $i++) {
$x = $i; //this helps me jump logical arguments without the use of else
// First Test
if (isset($val[$i+2]) && $x = $i) {
//Do a bunch of stuff
if (isset(the stuff done above)) {
// do other things and reset $i to jump through the array
$i=$i+2;
}
else {
unset($things);
unset($otherthings);
}
}
}
// Second Test
if (isset($val[$i+1]) && $x = $i) {
//Do a bunch of stuff
if (isset(the stuff done above)) {
// do other things and reset $i to jump through the array
$i=$i+1;
}
else {
unset($things);
unset($otherthings);
}
}
}
// Third and final test
if ($x = $i) {
//do other things
}
}
}
the Problem
I can't seem to understand why but the for loop or the IF statements (I am not 100% sure which one) fail to run through the first element of the array[0].
It runs fine from array[1] onward but even though i have tested that $x is indeed = to $i and therefore test 3 at the very least should work, the loop seems to run one loop past all the IF's and then start working from array[1].
What I have tried
I have changed for ($i=1; $i<$val; $i++) and this works fine (e.g. does not omit anything) from the start (but of course does not solve my problem as I am still missing array[0])
I have tested in the code if echo $val[0] prints out at the beginning of the code and it does
I have also tested $x = $i and these also work
It is one of those issues that feel too silly to change everything in my code but having searched a lot throughout stack overflow and google for similar issues, I cannot seem to find out why.
There must be something wrong I cannot see in the way I have written the loop?
Use $x == $i to test for equality, not $x = $i, which is an assignment.

Break first loop and continue next loop if true

I have a foreach loop inside another foreach loop as this code:
foreach($array1 as $uid => $somevalues) {
$status = true;
foreach($somevalues as $somevalue)
if(!isset($somevalue->$someothervalue))
$status = false;
if($status) {
$content .= "some content added";
$count++;
}
}
How it works
As you see I am looping through all entries in $array1. For each entry I loop through the contained $somevalues array to look for a certain condition (that $someothervalue exists there). Now, if this condition (that something isset) is not true then the $status changes to false.
Only if the $status is still true after all loops of the inner foreach, I wish some action to be made (some content added to a text string $content and a raised counter).
The goal
The arrays I'm working with can be quite large and I have been looking at the break and continue commands to try to skip unnecessary parts to improve the code and have the script running through fewer loops.
As you can see, if a loop from the inner foreach results in $status=false then we can stop it right there (no further loops here are necessary). The break is good for this to skip the rest of the forearch loops.
If this is the case then we can also skip the rest of the current outer loop from the outer foreach, since there will not be run any code now that $status is false. For this purpose the continue command would be useful to let us skip the rest of the current outer loop and go on to the next array row right away.
The question
I have been looking at the command lines break 2 and continue 2 e.g. that let you skip the current AND the outer loop (two steps up) with their respective results (either stopping the loop entirely or skipping the rest of current loop). They work as double break - as a "break break" - and as double continue - as a "continue continue" - respectively.
But here I need something like break continue instead, if you get my point. The first loop should break, the next should continue. I am trying to avoid reaching the if statement in the code if not necessary. The check is already made and it feels like doing an unnecessary double check.
I hope the question is clear. Is there a method for this purpose to double skip from within the inner foreach but with the effect of two different commands break continue?
"Flag" variables are mostly a code smell, and an indication that you actually should be using a function:
function all_valid($somevalues) {
foreach($somevalues as $somevalue)
if(!isset($somevalue->$someothervalue))
return false;
return true;
}
foreach($array1 as $uid => $somevalues) {
if(all_valid($somevalues) {
do stuff
If you're prepared to sacrifice readability for performance, then how about:
foreach($array1 as $uid => $somevalues) {
foreach($somevalues as $somevalue)
if(!isset($somevalue->$someothervalue))
goto next; // I don't believe I wrote this
$content .= "some content added";
$count++;
next:
}
Just use continue 2, it will 'break' out of the first loop and continue the outer loop.
foreach([1, 2, 3] as $i) {
echo $i . PHP_EOL;
foreach([4, 5, 6] as $ii) {
continue 2;
}
echo 'this will never print' . PHP_EOL;
}
Will print:
1
2
3
foreach($array1 as $uid => $somevalues) {
// Task of loop 1
foreach($somevalues as $somevalue){
// Task of loop 2
if(!isset($somevalue->$someothervalue))
{
continue 2;
break;
}
}
}

How to recurse a function itself?

For ex. If I have a function rand(0,2). How do i build a function such that
resc(100,rand(0,2));
Prints the value of rand(0,2) 100 times? For that matter. Any function that is printable.
I tried this.But doesnt seem to work.
<?php
function resc($i, $f) {
if ($i != 0) {
print $f;
return resc($i-1, $f);
} else {
print $f;
}
}
resc(4, rand(0, 1));
?>
If you just want to print a bunch of random ints:
function resc_int($recursions, callable $func, $args = array()) {
for($i = 0; $i < $recursions; $i++) {
echo call_user_func_array($func, $args);
}
}
// resc_int(100, "rand", array(0, 1));
Completely untested. Caveat Lector.
The way this works is that instead of using recursion (which will use more and more memory the more recursions you have, as you can't garbage collect something with active references or you will get a segmentation fault later in PHP if it tries to access it), it uses iteration.
There's two kinds of "looping" in a sense.
Looping/Recursion.
Technically Recursion is a loop, as you continue recursing until you reach a stop condition (else you get an infinite loop, which is bad for fairly painful reasons). The loop construct in PHP is while().
Iteration
In pretty much every single case (unless you have a really good reason) this is always the better choice when you need something that loops. Iteration is, to put it simply, counting up or down to a target integer. This is the for() construct; and hence, also the foreach() construct, which uses the number of elements in an array as a target integer and then counts towards it, picking elements out of the array as it goes.
This is more memory efficient, and is remarkably easy to work with in PHP. It can also infinite loop, like while can (just give it an impossible condition to work towards), so watch out for that.
In short: have fun with the standard library (pretty much every function you regularly need is somewhere in there), and remember that recursion is your last option, not the first.
function resc($times, $cbFtn, Array $cbFtnArgs = array()) {
if ($times != 0) {
print call_user_func_array($cbFtn, $cbFtnArgs);
resc($times - 1, $cbFtn, $cbFtnArgs);
}
}
resc(4, 'rand', array(0, 1));

Categories