I am learning PHP. I decided to adapt a solution to the famous FizzBuzz problem from Javascript to PHP, just to see how JS and PHP compare.
For those who forgot what the FizzBuzz problem is :
Write a short program that prints each number from 1 to 100 on a new
line. For each multiple of 3, print "Fizz" instead of the number.
For each multiple of 5, print "Buzz" instead of the number. For
numbers which are multiples of both 3 and 5, print "FizzBuzz" instead
of the number.
I am using a lot of short-circuit evaluations in the following examples.
Here's the clever solution (not written by me) that I adapted to PHP:
Works great!
Then, for the sake of challenge, I decided to try and rewrite it in one single line.
Here's how I started:
Seems like a good start. My condition in the while loop: if $i is not set set, then I set it to zero, but if it is already set, I skip the first part and check if it's inferior to 100.
As you can see on this picture, my loop works.
Since my goal is to write it in one line, I need to increment $i inside my conditional statement, just as with my previous multi-line solution. But when I write $i++ < 100 as before, something weird happens. My loop only runs once and stops.
Very weird indeed.
Even weirder, if I use both increments (one in the condition and one in the loop), the loop then works fine, and applies both increments.
I'm puzzled. Is it because there is a safeguard for infinite loops somewhere? With all those short-circuit evaluations, even my IDE PHP Storm says 'variable $i is probably undefined'. But it gets defined, and my loop works fine under certain conditions. What did I miss ?
EDIT:
Here's the code:
Multi-line working FizzBuzz:
$i = 0;
while ($i++ < 100) {
$msg = '';
($i % 3) || ($msg = $msg . 'Fizz');
($i % 5) || ($msg = $msg . 'Buzz');
echo $msg . "\n";
}
Weird loop iteration (you can delete the increment either in the loop or in the condition, or leave both to see the different effects):
while ( (!isset($i) && ($i=0 || true) ) || ($i++ < 100) ) {
echo $i . "\n";
$i = $i +1;
}
if $i is not set set, then I set it to zero
This is not quite right. Here's what's going on.
Your statement ($i=0 || true) sets $i to TRUE.
PHP's type juggling with print "1" for "$i". Consider $v = TRUE; echo "$v"; to see this in effect.
On the next iteration, your second condition is evaluated as TRUE < 100 which evaluates to FALSE, thereby exiting the loop.
So, in order to fix your problem, simply drop the || true and be on your merry way.
$i=0 || true results in $i being true.
true++ doesn’t actually do anything.
true < 100 is false.
echo true outputs 1.
An explicit true + 1 creates 2.
OMG, Yes! I thought I needed the || true part because ( !isset($i) && ( $i = 0 ) ) will either do both sides of the &&, or neither. I never thought that ( $i = 0 ) would evaluate to "true". But it looks like it does :)
OBSOLETE COMMENT: I found the origin of the quirk. Not sure why it happens though.
If I rewrite $i++ as ($i = $i + 1), it works fine.
while ( (!isset($i) && ($i=0 || true) ) || (($i = $i + 1) < 100) ) {
echo $i . "\n";
}
Related
I am running a for loop 10 times in order to populate data in a data table. In doing this, I wanted to use number_format in order to format the numbers. However, when I apply the number_format the For loop for some reason runs one additional time.
It works just fine when I exclude the number_format. Can anyone explain why this happens?
<?php
foreach($data['data'] as $result) {
For ($n = 0; $n <= 10; $n++){
echo "<td>";
echo number_format($result[$n], 0, ".", ",");
echo "</td>";
}
}
?>
TL;DR: Your loop will always run an additional time. Assuming that there are no errors in your number_format function call, all you have to do to get this to run 10 times is change your code to for($n = 0; $n < 10; n++). Note the use of < and not <=.
For loops are really just syntactical sugar for while loops. The statement for(initial_statement; bound_condition; loop_statement) { code; } is equivalent to
initial_statement;
while(bound_condition) {
code;
loop_statement;
}
Which, functionally, is equivalent to
initial_statement;
while(true) {
code;
loop_statement;
if(!bound_condition) break;
}
This means that if you want a loop to run, say, 2 times, and you write for($i = 0; $i <= 2; $i++) your code will loop as follows:
$i = 0
i++; (i now equals 1)
i <= 2 (condition is true, so continue)
$i = 1
i++; (i now equals 2)
i <= 2 (condition is true, so continue)
$i = 2
i++; (i now equals 3)
i <= 2 (condition is FALSE, so break)
Using the <= operator when your control variable starts at 0 causes an extra iteration to occur, since there are three integer values of i such that 0 <= i <= 2 (0, 1, and 2). To ensure that there are only two iterations, use the < operator, and now the loop will only be executed for values in the domain 0 <= i < 2 (0 and 1).
If you are still bent on using the <= operator and are fine with a non-zero-based iteration count, then you can simply change the initial value of i to 1 to offset the error.
By the way your code is written, I assume that you wish for your inner loop to run 10 times, not 11. This would explain why you are getting an extra iteration, and the issue is quite unrelated to the use of number_format. If you are only getting 10 iterations when you don't use that function, you might want to make sure that the statement 1 == 1 evaluates to true in your PHP interpreter.
Additionally, as a code styling issue, I would recommend using consistent case in your statements; you write foreach (lowercase) but also use For (uppercase). The convention is to use lowercase for both.
I have no clue why you would be only getting 10 iterations without number_format. You might be counting incorrectly? Try changing it to < and see if that resolves your issue.
I know this below statement works
for ($i= 0; $i=<10; $i++)
will output till 0 to 10
but when I write this code
for ($i=0; $i=10; $i++)
it print 10 for unlimited times... why why it not print 0 to 10...
what error I have done to get the result 0 to 10 for it....
The middle term in a for loop is the condition that says whether the loop should continue running. i=10 assigns 10 to i, and it also evaluates to the number 10, which is not zero, so it's considered true. Since the loop's condition is always true, it never stops running.
because i=10 always be true i==10 will be good
The second statement in the for loop is the condition. When its not true it will leave the loop. i=10 is not a comparator. i will be set to 10 every round that loop goes and because it "works" its resulting in true. i == 10 would be a comparator but i would never be 10 in its first round.
if $i< or =10 Will perform,if $i> 10 will break,each time $i will +1 you can set the $i begin to value or set the maximum to achieve the result that you want.
" = " sign is an assignment operator and can't be used as conditional where as >= , == , <= such operators are conditional, so while checking for a condition you should not use " = "
please write like this below
for($i=0; $i <= 10; $i++)
{
echo $i;
}
I've been programming in php for about 2 years now.
I just stumbled into this for loop:
// Check the URI namespace for a context
$wsDir = basename(dirname(__FILE__));
$uriArr = explode("/", $_SERVER['REQUEST_URI']);
for (
$i = 0, $uriSize = sizeof($uriArr);
$i < $uriSize && $uriArr[$i] != $wsDir && $i++;
);
$i++;
self::$executionContext = isset($uriArr[$i]) && !empty($uriArr[$i]) && substr($uriArr[$i], 0, 1) != '?'
? strtoupper($uriArr[$i]) : 'SOAP';
and I have no idea how this is supposed to work.
Can someone explain this to me ?
It is just a normal three-part for loop without its main statement and an empty third part.
From the manual:
for (expr1; expr2; expr3)
statement
The first expression (expr1) is evaluated (executed) once unconditionally at the beginning of the loop.
In the beginning of each iteration, expr2 is evaluated. If it evaluates to TRUE, the loop continues and the nested statement(s) are executed. If it evaluates to FALSE, the execution of the loop ends.
At the end of each iteration, expr3 is evaluated (executed).
So:
for (
# initializes two variables
$i = 0, $uriSize = sizeof($uriArr);
# conditional, expr2
$i < $uriSize && $uriArr[$i] != $wsDir && $i++;
# no expr3
);
If the expr2 evaluates to true the loop continues. Of course there is no statement or block to execute, so it just jumps to the next iterarion, meaning expr2 will be executed repeatedly until it evaluates to false at some point.
As pointed out by R. Chappell in the comments, this is probably to find a position in a string. You could rewrite this with a similar logic but in a more "descriptive" way:
$uriSize = sizeof($uriArr)
for ($i = 0; $i < $uriSize; $i++) {
if ($uriArr[$i] == $wsDir) break;
}
# now $i will be the index of the first $wsDir occurrence in the $uriArr array
Coming late, but none seems to have cached this : this for loop is equivalent to :
$i = 1;
Why ? Because in the condition part of the for loop, you have 3 conditions that are bound with AND:
$i < $uriSize
&&
$uriArr[$i] != $wsDir
&&
$i++;
In the first iteration, $i++ evaluates to 0 which is equivalent to false, and is incremented only after. So the loop stops after only one iteration, and $i is 1, and you have a bug. Unless it's a typo in your code...
This is another example (not an answer as such) of using a for without a third statement. It's a little clearer than the original question.
for ($i=0; $i >= $i++ && $i <= 10; /* third statement */ );
echo $i;
This will basically count to 10 and echo it out, and it's only made possible with the increment operator in PHP.
First we set $i to zero;
Second we check and increment $i to ensure it's equal to or greater than itself whilst less than or equal to 10.
Third we do nothing... no point really...
However, normal people would write the same thing as:
for ($i = 0; $i <= 10; $i++);
echo $i;
You'll have to imagine a better use case though and yes you can just do $i = 10; but it doesn't go as far as to explaining the question.
I have the following code in production that appears to be causing an infinite loop.
$z=1;
while (!$apns = $this->getApns($streamContext) && $z < 11)
{
myerror_log("unable to conncect to apple. sleep for 2 seconds and try again");
$z++;
sleep(2);
}
How are the precedence rules getting applied that cause this behavior?
http://php.net/manual/en/language.operators.precedence.php
I see this note in the docs:
Although = has a lower precedence than most other operators, PHP will
still allow expressions similar to the following: if (!$a = foo()), in
which case the return value of foo() is put into $a.
Which makes me think the the = should be evaluated first. then the ! then the &&, which would not cause an infinite loop.
Your code is evaluating like this:
while (!($apns = ($this->getApns($streamContext) && ($z < 11))))
which is why you see the infinite loop (as soon as $z >= 11, $apns is false, so the condition is always true). The reason for this precedence is that the special rules only apply to ! on the left of the assignment being valid (having lower precedence than =). It has no effect on the boolean operator on the right, which behaves as it would in any sane language.
Your style is bad. Try this, which is much more readable and only differs in the final value of $z (and if that's important you can tweak the break statement.
for( $z = 1; $z < 11; ++ $z ) {
// note extra brackets to make it clear that we intend to do assignment not comparison
if( ($apns = $this->getApns($streamContext)) ) {
break;
}
myerror_log("unable to conncect to apple. sleep for 2 seconds and try again");
sleep(2);
}
Your code is clear example of why it's good habit to always put all the conditions in brackets (and the same applies to code block. Even oneliners should be surrounded by { and }). So instead of error-prone:
while (!$apns = $this->getApns($streamContext) && $z < 11)
do
while (!($apns = $this->getApns($streamContext)) && ($z < 11))
and you will be safe.
for($i=0;$i<$num;$i++) {
if($i==even) $hilite="hilite";
dothing($i,$hilite);
}
This is basically what I want to accomplish.
What is the most efficient way to determine if $i is even?
I know I could check if half == mod 2 ... but that seems a little excessive on the calculations? Is there a simpler way?
if ($i % 2 == 0)
The already mentioned % 2 syntax is most used, and most readable for other programmers. If you really want to avoid an 'overhead' of calculations:
for($i = 0, $even = true; $i < $num; $i++, $even =! $even) {
if($even) $hilite = "hilite";
dothing($i,$hilite);
}
Although the assignment itself is probably more work then the '%2' (which is inherently just a bit-shift).
It doesn't get any simpler than $i % 2 == 0. Period.
Change the i++ in the loop statement to i+=2, so that you only examine even values of i?
Typically, a number is odd if it's LSB (Least Significant Bit) is set. You can check the state of this bit by using the bitwise AND operator:
if($testvar & 1){
// $testvar is odd
}else{
// $testvar is even
}
In your code above, a more efficient way would be to have $i increment by 2 in every loop (assuming you can ignore odd-values):
for($i=0;$i<$num;$i+=2){
// $i will always be even!
}