2

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:
enter image description here

Screenshot of probGen() failing:
enter image description here

From testing I've been able to determine that probGen() fails if the recursion occurs more than once.

Community
  • 1
  • 1
Tobi Alafin
  • 269
  • 2
  • 13
  • 3
    You need to return the probGen result in your else block. – aynber Jan 03 '19 at 15:58
  • @aynber I don't understand how that works? In my (admittedly limited) understanding `else` transfers control to `probGen()`, so nothing after the function call would get executed? Furthermore, shouldn't the completion of the program occur in the if block? I think it may be because I didn't decrement `$max` after the final assignment, so it would create an indefinite recursion/otherwise fail. I'm on mobile now, and would try that fix once I get back to my laptop. – Tobi Alafin Jan 03 '19 at 16:17
  • Instead of thinking of it as the same function, think of it as different functions. You call function 1, which will either return a value or call function 2. Function two returns a value to function 1, but function 1 never actually does anything with the response, so it gets lost in the ether. Doing `return probGen(...)` will return the response from the recursive function. Calling one function from another doesn't transfer control. Another way to look at it is you call friend #1 with a question. They don't know, so they call friend #2, and then have to call you back to tell you the answer. – aynber Jan 03 '19 at 16:22
  • @TobiAlafin adding `return` would return the result of each recursive call of `probGen()` to the one before, which will eventually return it to the initial function call. – Lewis Jan 03 '19 at 16:23
  • @aynber, Lewis thank you very much. I understand where I went wrong. Please format your comment as an answer so I can accept it. – Tobi Alafin Jan 03 '19 at 17:25

1 Answers1

1

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.

dWinder
  • 11,597
  • 3
  • 24
  • 39