I got really basic question about variables and function calls. I don't know how to name this question, so couldn't find any answer using search...
So is there somekind performance difference between these two codes:
Example 1:
if($Page->getCurrentPage('id') == 1) {
foreach($Page->getPagePosts() as $key => $pagePost) {
include(PATH_TEMPLATES. "post.php");
if(count($Page->getPagePosts()) - 1 > $key) {
echo "<hr>";
}
}
}
Example 2:
$arr = $Page->getPagePosts();
if($Page->getCurrentPage('id') == 1) {
foreach($arr as $key => $pagePost) {
include(PATH_TEMPLATES. "post.php");
if(count($arr) - 1 > $key) {
echo "<hr>";
}
}
}
Previously I have used Example 2, but not I started thinking if Example 1 is correct too.
It depends on your application and scale. Basically it is strongly recommended to ignore trivial performance optimization for the sake of better readability, scalability and maintainability. But there might be instances where you are going to iterate for 1000 times, which then, come costly if you ignore certain standards.
About your first example, in small cases it is OK, but in large calculations, it is best to avoid function-call in any type of loop, and it is best to pass an array to them as there would be no function overhead.
Therefore,
foreach( $users as $user)
{
//....
}
is better than
foreach( $this->getUsers() as $user)
{
// ....
}
But there are cases where you can simply ignore such seriousness, for example, in case your site only have two different logos, or at most, 5-records in a table, you can still stick to function-call in the loops:
foreach( $this->siteLogos() as $logo )
{
// ....
}
While I always read that using count() in for-loop must be avoided.
Online benchmarks say that foreach is faster than for-loops, which I'm not sure and invite you to have a research on it.
Related
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));
I've recently stumbled over this code:
function xrange($min, $max)
{
for ($i = $min; $i <= $max; $i++) {
yield $i;
}
}
I've never seen this yield keyword before. Trying to run the code I get
Parse error: syntax error, unexpected T_VARIABLE on line x
So what is this yield keyword? Is it even valid PHP? And if it is, how do I use it?
What is yield?
The yield keyword returns data from a generator function:
The heart of a generator function is the yield keyword. In its simplest form, a yield statement looks much like a return statement, except that instead of stopping execution of the function and returning, yield instead provides a value to the code looping over the generator and pauses execution of the generator function.
What is a generator function?
A generator function is effectively a more compact and efficient way to write an Iterator. It allows you to define a function (your xrange) that will calculate and return values while you are looping over it:
function xrange($min, $max) {
for ($i = $min; $i <= $max; $i++) {
yield $i;
}
}
[…]
foreach (xrange(1, 10) as $key => $value) {
echo "$key => $value", PHP_EOL;
}
This would create the following output:
0 => 1
1 => 2
…
9 => 10
You can also control the $key in the foreach by using
yield $someKey => $someValue;
In the generator function, $someKey is whatever you want appear for $key and $someValue being the value in $val. In the question's example that's $i.
What's the difference to normal functions?
Now you might wonder why we are not simply using PHP's native range function to achieve that output. And right you are. The output would be the same. The difference is how we got there.
When we use range PHP, will execute it, create the entire array of numbers in memory and return that entire array to the foreach loop which will then go over it and output the values. In other words, the foreach will operate on the array itself. The range function and the foreach only "talk" once. Think of it like getting a package in the mail. The delivery guy will hand you the package and leave. And then you unwrap the entire package, taking out whatever is in there.
When we use the generator function, PHP will step into the function and execute it until it either meets the end or a yield keyword. When it meets a yield, it will then return whatever is the value at that time to the outer loop. Then it goes back into the generator function and continues from where it yielded. Since your xrange holds a for loop, it will execute and yield until $max was reached. Think of it like the foreach and the generator playing ping pong.
Why do I need that?
Obviously, generators can be used to work around memory limits. Depending on your environment, doing a range(1, 1000000) will fatal your script whereas the same with a generator will just work fine. Or as Wikipedia puts it:
Because generators compute their yielded values only on demand, they are useful for representing sequences that would be expensive or impossible to compute at once. These include e.g. infinite sequences and live data streams.
Generators are also supposed to be pretty fast. But keep in mind that when we are talking about fast, we are usually talking in very small numbers. So before you now run off and change all your code to use generators, do a benchmark to see where it makes sense.
Another Use Case for Generators is asynchronous coroutines. The yield keyword does not only return values but it also accepts them. For details on this, see the two excellent blog posts linked below.
Since when can I use yield?
Generators have been introduced in PHP 5.5. Trying to use yield before that version will result in various parse errors, depending on the code that follows the keyword. So if you get a parse error from that code, update your PHP.
Sources and further reading:
Official docs
The original RFC
kelunik's blog: An introduction to generators
ircmaxell's blog: What generators can do for you
NikiC's blog: Cooperative multitasking using coroutines in PHP
Co-operative PHP Multitasking
What is the difference between a generator and an array?
Wikipedia on Generators in general
This function is using yield:
function a($items) {
foreach ($items as $item) {
yield $item + 1;
}
}
It is almost the same as this one without:
function b($items) {
$result = [];
foreach ($items as $item) {
$result[] = $item + 1;
}
return $result;
}
The only one difference is that a() returns a generator and b() just a simple array. You can iterate on both.
Also, the first one does not allocate a full array and is therefore less memory-demanding.
simple example
<?php
echo '#start main# ';
function a(){
echo '{start[';
for($i=1; $i<=9; $i++)
yield $i;
echo ']end} ';
}
foreach(a() as $v)
echo $v.',';
echo '#end main#';
?>
output
#start main# {start[1,2,3,4,5,6,7,8,9,]end} #end main#
advanced example
<?php
echo '#start main# ';
function a(){
echo '{start[';
for($i=1; $i<=9; $i++)
yield $i;
echo ']end} ';
}
foreach(a() as $k => $v){
if($k === 5)
break;
echo $k.'=>'.$v.',';
}
echo '#end main#';
?>
output
#start main# {start[0=>1,1=>2,2=>3,3=>4,4=>5,#end main#
yield keyword serves for definition of "generators" in PHP 5.5.
Ok, then what is a generator?
From php.net:
Generators provide an easy way to implement simple iterators without the overhead or complexity of implementing a class that implements the Iterator interface.
A generator allows you to write code that uses foreach to iterate over a set of data without needing to build an array in memory, which may cause you to exceed a memory limit, or require a considerable amount of processing time to generate. Instead, you can write a generator function, which is the same as a normal function, except that instead of returning once, a generator can yield as many times as it needs to in order to provide the values to be iterated over.
From this place: generators = generators, other functions (just a simple functions) = functions.
So, they are useful when:
you need to do things simple (or simple things);
generator is really much simplier then implementing the Iterator interface. other hand is, ofcource, that generators are less functional. compare them.
you need to generate BIG amounts of data - saving memory;
actually to save memory we can just generate needed data via functions for every loop iteration, and after iteration utilize garbage. so here main points is - clear code and probably performance. see what is better for your needs.
you need to generate sequence, which depends on intermediate values;
this is extending of the previous thought. generators can make things easier in comparison with functions. check Fibonacci example, and try to make sequence without generator. Also generators can work faster is this case, at least because of storing intermediate values in local variables;
you need to improve performance.
they can work faster then functions in some cases (see previous benefit);
None of the answers show a concrete example using massive arrays populated by non-numeric members. Here is an example using an array generated by explode() on a large .txt file (262MB in my use case):
<?php
ini_set('memory_limit','1000M');
echo "Starting memory usage: " . memory_get_usage() . "<br>";
$path = './file.txt';
$content = file_get_contents($path);
foreach(explode("\n", $content) as $ex) {
$ex = trim($ex);
}
echo "Final memory usage: " . memory_get_usage();
The output was:
Starting memory usage: 415160
Final memory usage: 270948256
Now compare that to a similar script, using the yield keyword:
<?php
ini_set('memory_limit','1000M');
echo "Starting memory usage: " . memory_get_usage() . "<br>";
function x() {
$path = './file.txt';
$content = file_get_contents($path);
foreach(explode("\n", $content) as $x) {
yield $x;
}
}
foreach(x() as $ex) {
$ex = trim($ex);
}
echo "Final memory usage: " . memory_get_usage();
The output for this script was:
Starting memory usage: 415152
Final memory usage: 415616
Clearly memory usage savings were considerable (ΔMemoryUsage -----> ~270.5 MB in first example, ~450B in second example).
With yield you can easily describe the breakpoints between multiple tasks in a single function. That's all, there is nothing special about it.
$closure = function ($injected1, $injected2, ...){
$returned = array();
//task1 on $injected1
$returned[] = $returned1;
//I need a breakpoint here!!!!!!!!!!!!!!!!!!!!!!!!!
//task2 on $injected2
$returned[] = $returned2;
//...
return $returned;
};
$returned = $closure($injected1, $injected2, ...);
If task1 and task2 are highly related, but you need a breakpoint between them to do something else:
free memory between processing database rows
run other tasks which provide dependency to the next task, but which are unrelated by understanding the current code
doing async calls and wait for the results
and so on ...
then generators are the best solution, because you don't have to split up your code into many closures or mix it with other code, or use callbacks, etc... You just use yield to add a breakpoint, and you can continue from that breakpoint if you are ready.
Add breakpoint without generators:
$closure1 = function ($injected1){
//task1 on $injected1
return $returned1;
};
$closure2 = function ($injected2){
//task2 on $injected2
return $returned1;
};
//...
$returned1 = $closure1($injected1);
//breakpoint between task1 and task2
$returned2 = $closure2($injected2);
//...
Add breakpoint with generators
$closure = function (){
$injected1 = yield;
//task1 on $injected1
$injected2 = (yield($returned1));
//task2 on $injected2
$injected3 = (yield($returned2));
//...
yield($returnedN);
};
$generator = $closure();
$returned1 = $generator->send($injected1);
//breakpoint between task1 and task2
$returned2 = $generator->send($injected2);
//...
$returnedN = $generator->send($injectedN);
note: It is easy to make mistake with generators, so always write unit tests before you implement them!
note2: Using generators in an infinite loop is like writing a closure which has infinite length...
An interesting aspect, which worth to be discussed here, is yielding by reference. Every time we need to change a parameter such that it is reflected outside of the function, we have to pass this parameter by reference. To apply this to generators, we simply prepend an ampersand & to the name of the generator and to the variable used in the iteration:
<?php
/**
* Yields by reference.
* #param int $from
*/
function &counter($from) {
while ($from > 0) {
yield $from;
}
}
foreach (counter(100) as &$value) {
$value--;
echo $value . '...';
}
// Output: 99...98...97...96...95...
The above example shows how changing the iterated values within the foreach loop changes the $from variable within the generator. This is because $from is yielded by reference due to the ampersand before the generator name. Because of that, the $value variable within the foreach loop is a reference to the $from variable within the generator function.
The below code illustrates how using a generator returns a result before completion, unlike the traditional non generator approach that returns a complete array after full iteration. With the generator below, the values are returned when ready, no need to wait for an array to be completely filled:
<?php
function sleepiterate($length) {
for ($i=0; $i < $length; $i++) {
sleep(2);
yield $i;
}
}
foreach (sleepiterate(5) as $i) {
echo $i, PHP_EOL;
}
When implement PHP IteratorAggregate interface, the yield keyword will be useful. Check out the documentation, there are couple examples either using ArrayIterator or yield.
Another example can be found is in the php-ds/polyfill repo: https://github.com/php-ds/polyfill/blob/e52796c50aac6e6cfa6a0e8182943027bacbe187/src/Traits/GenericSequence.php#L359
The idea is similar to the quick example below:
class Collection implements \IteratorAggregate
{
private $array = [];
public function push(...$values)
{
array_push($this->array, ...$values);
}
public function getIterator()
{
foreach ($this->array as $value) {
yield $value;
}
}
}
$collection = new Collection();
$collection->push('apple', 'orange', 'banana');
foreach ($collection as $key => $value) {
echo sprintf("[%s] => %s\n", $key, $value);
}
Output:
[0] => apple
[1] => orange
[2] => banana
Does anyone know of any issues, performance or other, that can occur by casting a variable to an array instead checking it first?
// $v could be a array or string
$v = array('1','2','3');
OR
$v = '1';
instead of:
if (is_array($v)) foreach ($v as $value) {/* do this */} else {/* do this */}
I've started to use:
foreach((array) $v as $value) {
// do this
}
It stops the repetition of code quite a bit - but performance is on my mind, not ugly code.
Also, does anyone know how php handles casting an array to an array? No errors are thrown but does the php engine check if it's a array, then return a result before doing the cast process?
First: Premature optimization is the root of all evil. Never let performance influence your coding style!
Casting to an array allows to some nice tricks, when you need an array, but want to allow a single value
$a = (array) "value"; // => array("value")
Note, that this may lead to some unwanted (or wanted, but different) behaviours
$a = new stdClass;
$a->foo = 'bar';
$a = (array) $a; // => array("foo" => "bar");
However, this one
if(is_array($v)) {
foreach($v as $whatever)
{
/* .. */
}
} else {
/* .. */
}
allows you to decide what should happen for every single type, that can occur. This isn't possible, if you cast it "blindly" to an array.
In short: Just choose the one, that better fits the needs of the situation.
As Felix Kling says above, the best solution is to have your data come from a source that guarantees its type. However, if you can't do that, here's a comparison for 1000000 iterations:
check first: 2.244537115097s
just cast: 1.9428250789642s
(source)
Just casting without checking (using in_array) seems to be (slightly) quicker.
Another option here is this (this can be done inline as well, of course, to avoid the function call):
function is_array($array) {
return ($array."" === "Array");
}
This seems to be slightly faster than is_array, but your mileage may vary.
The problem with casting to an array like this (which is also an option)
if ((array) $v === $v)
Is that it's faster than is_array for small arrays, but disastrously slower for large ones.
Is there any good alternative for the plain if statements in PHP? I know about switch, but I'll guess that there's some more refined alternative out there that comes handy when working with really big if statements.
Thanks a lot,
If you can't read your algorithm on one screen fold, there's a 99.9% chance you need to refactor your code toward more readability.
Change
if ($isHappening) {
// ... millions of lines of code
} else {
// .. another million lines of code
}
into
if ($isHappening) {
happen();
} else {
didntHappen();
}
function happen() {
// millions of lines of code
}
function didntHappen() {
// another million lines of code
}
There really is no magic hammer out there. Your best bet to making them manageable is to break nested ifs into their own functions to make them more readable.
Also, don't forget about array_filter. That can save you from having to write a for loop to filter out items.
Also, you can eliminate nesting by using guard statements. You basically invert your if and do a return instead (another reason to break conditions into functions).
If you want to improve readability only, then you can always split up the expressions inside the if statement:
$exp1 = is_array($var) && isset($var['key']);
$exp2 = is_object($var) && isset($var->key);
$exp3 = substr($string, 0, 4) == 'foo';
$exp4 = ($exp1 || $exp2) && $exp3;
if ($exp4) {}
instead of
if (((is_array($var) && isset($var['key'])) || (is_object($var) && isset($var->key))) && substr($string, 0, 4) == 'foo') {}
Obviously, these are simplified examples, but you get the idea...
Welcome to the world of Object Orientation :)
class Case1 {
function do() { echo "case 1"; }
}
class Case2 {
function do() { echo "case 2"; }
}
$object = new Case1();
$object->do();
And then, there is dispatching using an array:
$choices = array( "case1" => new Case1(), "case2" => new Case2(), ... );
$choices[ $_GET["case"] ]->do();
Well if is if, there is not much else out there. Of course switch is an alternative but depending on the conditions it might not be applicable.
If you are doing OOP, the state design pattern might be what you need.
Otherwise you have to give more information...
If by "big" you mean large, highly nested "ifs", this is a clear sign of code smell, and you should be looking at OOP and design patterns.
I have this function that I wrote that is abysmally slow since php does not handle recursion well. I am trying to convert it to a while loop, but am having trouble wrapping my head around how to do it.
Can anyone give me some tips?
public function findRoute($curLoc, $distanceSoFar, $expectedValue) {
$this->locationsVisited[$curLoc] = true;
$expectedValue += $this->locationsArray[$curLoc]*$distanceSoFar;
$at_end = true;
for($i = 1; $i < $this->numLocations; $i++) {
if($this->locationsVisited[$i] == false) {
$at_end = false;
if($expectedValue < $this->bestEV)
$this->findRoute($i, $distanceSoFar + $this->distanceArray[$curLoc][$i], $expectedValue);
}
}
$this->locationsVisited[$curLoc] = false;
if($at_end) {
if($expectedValue < $this->bestEV) {
$this->bestEV = $expectedValue;
}
}
}
I'm not going to convert your code, but you can convert a recusive function into an iterative one by creating a stack:
$stack= array();
And instead of invoking $this->findroute(), push your parameters onto this stack:
$stack[] = array($i, $distanceSoFar + $this->distanceArray[$curLoc][$i], $expectedValue);
Now surround basically everything in your function into a while loop draining the stack after having primed it:
while ($stack) {
// Do stuff you already do in your function here
You can convert a recursive function into an iterative function by using a stack to store the current state. Look into array_push() and array_pop().
At a glance I don't think recursion is your problem, yes it's slow in PHP, but It looks like your going over values more than you need to, putting the values in a stack, or several stacks and handling them, may be nice.
custom sort functions have always helped me with problems of this sort.
function sort_by_visited($x,$y)
{
return ($this->locationsVisited[$x] > $this->locationsVisited[$y]) ? -1 : 1;
}
uasort($locationsVisited,'sort_by_visited');
This will prioritize all not visited locations at the top of the stack.
This looks like your trying to find the optimal route for traversal of a series of nodes in a graph.
I'm guessing that you've not studied Computer Science as the "Travelling Salesman" problem is an archetype of Artificial Intelligence. Of course, as such, it has its own Wikipedia page:
http://en.wikipedia.org/wiki/Travelling_salesman_problem
Sorry - but just swapping from a recursive to an iterative function isn't going to make it go any faster ("php does not handle recursion well." - can you provide reference for this assertion). If you need a faster solution then you'll need to look at non-exhaustive/fuzzy methods.
C.