This question already has answers here:
How to use return inside a recursive function in PHP
(4 answers)
Closed 9 months ago.
I am new to PHP. I have a function that works (the value printed to the screen from within the function is exactly what I expect), but only sometimes returns the answer (other times it returns NULL). I believe I have isolated the error to something involving my use of the static feature of PHP, but I am not sure how exactly I'm transgressing/how to fix it. I tried to work around it by creating a new non static variable to store the result, and that improved my code from always returning NULL to only sometimes returning NULL. The erring function is part of a larger suite of programs I wrote, so I'll include it and the testing function I use to check that it works.
probGen.php
<?
require_once("randX.php");
require_once("../displayArray.php");
error_reporting(E_ERROR | E_WARNING | E_PARSE);
function probGen(array $arr, float $control = 0.01)
/*
* Generates a valid, random probability distribution for a given array of elements, that can be used in conjunction with "probSelect()".
* Input:
$arr: An array of elements.
$control: A value that decides how much mass is allowed to be unilaterally dumped onto one element. A high value would permit distributions where most of the mass is concentrated on one element.
If an invalid value is provided, the default is used.
* Output: An associative array where the keys are the elements in the original array, and the values are their probabilities.
*/
{
$control = ($control <= 1 && $control >= 0)?($control):(0.01); #Use the default value if an invalid number is supplied.
static $result = []; #Initialises $result with an empty array on first function call.
static $max = 1; #Initialises $max with 1 on first function call.
foreach ($arr as $value)
{
$x = randX(0, $max); #Random probability value.
$result[$value] = ($result[$value] + $x)??0; #Initialise the array with 0 on first call, and on subsequent calls increment by $x to assign probability mass.
$max -= $x; #Ensures that the probability never sums to more than one.
}
print("<br>sum = ".array_sum($result)."<br><br>");
displayArray($result);
/*
* After the execution of the above code, there would be some leftover probability mass.
* The code below adds it to a random element.
*/
$var = array_values($arr);
if($max <= $control) #To limit concentration of most of the probability mass in one variable.
{
$result[$var[rand(0,(count($var)-1))]] += $max; #Selects a random key and adds $max to it.
displayArray($result);
print("<br>sum = ".array_sum($result)."<br><br>");
$sol = $result;
return $sol;
}
else
probGen($arr, $control);
}
?>
test.php
<?
/*
* This file contains some functions that can be used for assigning probabilities to an array of elements or selecting an element from said array with a given probability.
*/
require_once("../displayArray.php");
require_once("confirm.php");
require_once("randX.php");
require_once("probGen.php");
require_once("probSelect.php");
$test = ["California" => 0.37, "Florida" => 0.13, "New York" => 0.28, "Washington" => 0.22];
$testX = array_keys($test);
var_dump(probGen($testX));
/*for ($i=0; $i < 100 ; $i++)
{
print(var_dump(confirm(probGen($testX))));
}*/
?>
Screenshot of probGen() working as intended:
Screenshot of probGen() failing:
From testing I've been able to determine that probGen() fails if the recursion occurs more than once.
As #aynber comment (all credit to him I just wrote that so this post will have an answer).
When calling the recursive call you should add return (at the last line) in order to bubble the return value of probGen function as return probGen();
Generally speaking, having PHP return NULL sometimes may use as hint to case when non value is being returned.
Can see also this question with same issue.
Related
I need a program in PHP, when I search a number in a given series like 1,3,7,15,31... and if it is present in the series then give output as the index on which it is present in series?
LIKE I HAVE DONE SOMETHING TO DO THIS BUT FAILED.
<?php
function test1($n) {
for($i=1;$i<=$n;$i=$c) {
$c =1 + (2 * $i);
}
}
function test2($p) {
global $c,$n;
$input=array(1);
$in=array_push($input,$c);
$k=array_search($p,$input);
$flipped = array_flip($k);
var_dump($flipped);
}
test1(1000000);
test2(45);
Like in this program I had made two functions and in FUNCTION test1 I made a formula to make the series 1,3,7,15,31,63,127.... and in FUNCTION test2 I insert a number in form of parameter and want to SEARCH that number in the series that I form above and the I want OUTPUT as the index of that number searched.
Also if the number is not present in the series then I want the output as the nearest number of the number I search.
HELP.!!!
Thank You
You've got a few problems with this code.
function test1($n) {
for($i=1;$i<=$n;$i=$c) {
$c =1 + (2 * $i);
}
}
The first problem here is that you don't do anything with $c each time you increment it. You should probably be pushing it into an array of series integers.
Secondly you don't return a result, so you can't actually use the series you would've created.
You could use something like this instead:
function test1($limit) {
$series = [];
for ($i = 1; $i <= $limit; $i = $i * 2 + 1) {
$series[] = $i;
}
return $series;
}
Next, your test2 function:
function test2($p) {
global $c,$n;
$input=array(1);
$in=array_push($input,$c);
$k=array_search($p,$input);
$flipped = array_flip($k);
var_dump($flipped);
}
Ok, first don't use global variables. Pass the ones you need in as arguments and again, return the result. To be perfectly honest, I'm not entirely sure what this function is supposed to do. All you need is the array_search call which "searches the array for a given value and returns the first corresponding key if successful".
For example:
function test2($series, $number) {
return array_search($number, $series);
}
Using these, you can do something like this:
$series = test1(1000000);
var_dump(test2($series, 45)); // bool(false)
var_dump(test2($series, 31)); // int(4)
Also if the number is not present in the series then I want the output as the nearest number of the number I search.
Ok, you'll need to write some custom logic for this. I suggest you run your array_search check, then if it returns false you loop through your series and check the following criteria:
The previous series entry is lower than your number
The next series entry is higher than your number
Then return whichever of those two has a smaller absolute difference when you subtract the series entry from your number.
I'm not going to write an example for this because it smells a bit like a school assignment, which I'm sure you're capable of doing =) good luck.
I have run into a point in my code where I would like to check if, within a dynamically set array, there are at least two set values before performing certain tasks. I cannot find a native PHP function which accomplishes this. Count returns all values, but does not allow me to check for the "truthiness" of the array values. Is there an equivalent native function for the following code:
function count_set($array = array()){
$count = 0;
foreach($array as $key => $value){
if($value){
$count++;
}
}
return $count;
}
Truthy scalar values are NOT false, 0, null, string 0 or an empty string (see Converting to boolean). array_filter() will remove these by default if you don't provide a callback:
$count = count(array_filter($array));
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
I am writing a small command line application in php.
What is the correct way to handle command line arguments and options?
There seems to be the argv array, $_SERVER['argv'], and getopt but its confusing when to use each?
Also with regards to options i.e. "argument --option" what is the best way to get these?
Arguments, made easy
One day I decided to defeat this monster once and for all. I forged a secret weapon - a function that acts as a storage, a parser and a query function for arguments.
// You can initialize it with a multiline string:
arg("
-a --alpha bool Some explanation about this option
-b --beta bool Beta has some notes too
-n --number int Some number you need for the script
- --douglas int There is no short form of this
-o --others str A string of other things
");
// ... and now you have your arguments nicely wrapped up:
print arg("alpha"); // returns the value of -a or --alpha
print arg("a"); // same thing
print arg(); // returns the whole parsed array
print arg(1); // returns the first unnamed argument
print arg(2); // returns the second unnamed argument
print arg("douglas",42); // value of "douglas", or a reasonable default
Explanation
All you need to do is write the argument list as a multiline string. Four columns, looks like a help, but arg() parses your lines and finds out the arguments automatically.
Separate columns by two or more spaces - just like you would anyway.
Once parsed, each item will be represented by an array of fields, named char, word, type and help, respectively. If there's no short (char) or long (word) version for a parameter, just use a dash. Not for both, obviously.
Types are what they seem: bool means there's no value after the parameter; it's false if missing, true if present. The int and str types mean there must be a value, and int makes sure it's an integer. Optional parameters are not supported. Values can be separated by space or equal sign (i.e. "-a=4" or "-a 4")
After this first call, you have all your arguments neatly organized in a structure (dump it, you'll see) and you can query their values by name or number.
Function arg() has a second parameter for defaults so you'll never have to worry about missing values.
The arg() function itself
function arg($x="",$default=null) {
static $arginfo = [];
/* helper */ $contains = function($h,$n) {return (false!==strpos($h,$n));};
/* helper */ $valuesOf = function($s) {return explode(",",$s);};
// called with a multiline string --> parse arguments
if($contains($x,"\n")) {
// parse multiline text input
$args = $GLOBALS["argv"] ?: [];
$rows = preg_split('/\s*\n\s*/',trim($x));
$data = $valuesOf("char,word,type,help");
foreach($rows as $row) {
list($char,$word,$type,$help) = preg_split('/\s\s+/',$row);
$char = trim($char,"-");
$word = trim($word,"-");
$key = $word ?: $char ?: ""; if($key==="") continue;
$arginfo[$key] = compact($data);
$arginfo[$key]["value"] = null;
}
$nr = 0;
while($args) {
$x = array_shift($args); if($x[0]<>"-") {$arginfo[$nr++]["value"]=$x;continue;}
$x = ltrim($x,"-");
$v = null; if($contains($x,"=")) list($x,$v) = explode("=",$x,2);
$k = "";foreach($arginfo as $k=>$arg) if(($arg["char"]==$x)||($arg["word"]==$x)) break;
$t = $arginfo[$k]["type"];
switch($t) {
case "bool" : $v = true; break;
case "str" : if(is_null($v)) $v = array_shift($args); break;
case "int" : if(is_null($v)) $v = array_shift($args); $v = intval($v); break;
}
$arginfo[$k]["value"] = $v;
}
return $arginfo;
}
// called with a question --> read argument value
if($x==="") return $arginfo;
if(isset($arginfo[$x]["value"])) return $arginfo[$x]["value"];
return $default;
}
I hope this helps a lot of lost souls out there, like I was. May this little function shed a light upon the beauty of not having to write a help AND a parser and keeping them in sync... Also, once parsed, this approach is lightning fast since it caches the variables so you can call it as many times as you want. It acts like a superglobal.
Also available on my GitHub Gist.
You can retrieve the "raw" arguments using $argv.
See also: http://www.php.net/manual/de/reserved.variables.argv.php
Example: php file.php a b c
$argv will contain "file.php", "a", "b" and "c".
Use getopts to get the parameters "parsed", PHP will do the dirty job for you. So it's probably the best way to go in your case as you want to pass the parameters with --options.
Have a close look at http://www.php.net/manual/de/function.getopt.php
It describes the function well.
`I have a function called lets say calculate; what i want to do is run some loops with in the function calculating an outcome. at times this function does fail as it will get stuck in a loop, so iv got it covered to exit the loop but my problem is i want to restart this function and hope it come out with an outcome if not i will try again... on average the request does not have an outcome around 1 in 20, i need to restart the function from a clean slate.
i have tried to unset all the vars before i rerun the process with out success.please note this function will fail at times from the information handed to the process, un avoidable so
when this accurs i just want to rerun the function automatically to generate an outcome.
http://www.gamezslave.com/test/DynamicSlots.swf this is my test prototype give you an idea
sometimes you refresh it will error because of this factor.
<?php
$checker = 0; // if i cant get a result i could use this will tick up until condition
function shuffleArray($myArray) {
$value_count = array_count_values($myArray);
$last_value = $myArray[count($myArray) - 1];
unset($myArray[count($myArray) - 1]);
$shuffle = array();
$last = false;
while (count($myArray) > 0) {
$keys = array_keys($myArray);
$i = round(rand(0, count($keys) - 1));
while ($last === $myArray[$keys[$i]] ) {
$i = round(rand(0, count($keys) - 1));
echo "stuck";
$checker++;
if($checker>10){
echo " Too many checks so die, and restart process ";
return false;
bob; // this is the check function to goto and restart
}
}
$shuffle[] = $myArray[$keys[$i]];
$last = $myArray[$keys[$i]];
unset($myArray[$keys[$i]]);
}
if ($last_value === $last) {
$i = 0;
foreach($shuffle as $key=>$value) {
if ($value !== $last_value) {
$i = $key;
break;
}
}
array_slice($shuffle, $i + 1, 0, $last_value);
} else {
$shuffle[] = $last_value;
}
return $shuffle;
}
print_r(shuffleArray(array(1,5,5,3,7,7,7,7))); // just a example
function bob(){
if($checker>10){
$checker = 0;
shuffleArray();
echo "bob";
reset($myArray); // thought this may clean/reset the array i couldnt use
}
}
The idea this shuffle returns that no two symbols elemts of the same will be next to each other but sometimes at the end of the array as its shuffling randomly im left with bad odds (apple, orange, orange, orange) so what i need to do is resart this process again from start take in mind there is about 10 different item in the array and duplicates of each for example 10 apples ,10 oranges 4 bannanas 3 grapes sometimes the shuffle manages to generate an outcome where im stuck with too many of the same item at the end of the array wich then i need to rerun the script(and this bit is the problem) i dont know how.
Are you using globals?
If yes: Stop that. It's terrible practice. Seriously. There's never a reason to use globals with any sort of sane code.
If no: There's nothing to do. Each function invocation is a clean slate.
EDIT After seeing the code, I'm kinda speechless.
http://us3.php.net/manual/en/function.shuffle.php
I would set defaults to all variables inside of your function, and pass anything active as a parameter. Then use a set of return codes to indicate success or failure, or validate the outcome in some way and re-run.
I'm agreeing with Tyler Eaves here and i wanted to add as another reply a very important topic in programming:
A function "should" in theory be as scalar as possible meaning that it should not affect the outside world and should return the same information everytime for the same parameters.
If you call a function with parameter X, Y and Z and call it again while your system is in the same state, the function should return the exact same result.
But if a function is not scalar (Virtual for instance), such as dependant on external data (files, database rows or single instance accessible class data) then you should theorically not affect that external data from this function or else calling the function multiple times yield different results...