I want to understand why this function doesn't return anything.
function fact($n, $p = 1) {
if ($n > 1) {
$p *= $n--;
fact($n, $p);
} else {
return $p;
}
}
var_dump(fact(5)); // NULL
Recursion is a looping construct which comes from functional languages. So yes, as others noted, your function doesn't work correctly because the true branch of your if
statement doesn't return anything. However, I have additional remarks about your code
function fact($n, $p = 1) {
if ($n > 1) {
// this makes it hard to reason about your code
$p *= $n--;
return fact($n, $p);
} else {
return $p;
}
}
You're actually mutating two variables here in one expression. It's clever if you're trying to keep the code looking shorter, but there's actually an even better way.
function fact($n, $p = 1) {
if ($n > 1) {
$p *= $n--;
// just compute the next values; no need to update $p or $n
return fact($n - 1, $p * $n);
} else {
return $p;
}
}
Now we don't have to think about how $p
and $n
change individually. We just know that we call fact
again with the next values for each state of $p
and $n
.
Keep in mind, these principles are so strong in some functional programming languages that reassignment of variables likes $p
and $n
is not even allowed.
Lastly, we have to talk about your your API leak, $p
. If someone were to specify a value when calling fact
, they could get the wrong answer or trigger an error
// bad !
fact(5, 10); // => 1200
This is only possible because $p
is actually exposed in the public API. To get around this, you have a couple of options
One of them is to do as @RonaldSwets proposes:
function fact($n) {
// 1 is the base case, like you had for $p in your code
if ($n == 0)
return 1;
// otherwise return $n times the next value
else
return $n * fact($n - 1);
}
Another is to use an auxiliary function which is meant only for private use
// function used by `fact`
function fact_aux ($n, $p) {
if ($n == 0)
return $p;
else
return fact_aux($n - 1, $p * $n);
}
// function meant to be used by others
function fact ($n) {
return fact_aux($n, 1);
}