谁能用外行语言和示例向我解释PHP中的递归函数(不使用Fibonacci)?我在看一个例子,但斐波那契完全让我迷失了!
预先谢谢您;-)您还经常在Web开发中使用它们吗?
谁能用外行语言和示例向我解释PHP中的递归函数(不使用Fibonacci)?我在看一个例子,但斐波那契完全让我迷失了!
预先谢谢您;-)您还经常在Web开发中使用它们吗?
Answers:
递归函数是一个调用自身的函数
如果该函数不断调用自身,它如何知道何时停止?您设置了一个条件,称为基本情况。基本案例告诉我们递归调用何时停止,否则它将无限循环。
因为我在数学方面有很强的背景,对我来说,这是一个很好的学习例子,是阶乘的。通过下面的评论,似乎阶乘函数可能有点太多,我将其保留在此处,以防万一您需要它。
function fact($n) {
if ($n === 0) { // our base case
return 1;
}
else {
return $n * fact($n-1); // <--calling itself.
}
}
关于在Web开发中使用递归函数,我个人不求助于使用递归调用。并不是说依赖于递归是不好的做法,但是它们不应该是您的首选。如果使用不当,它们可能会致命。
尽管我无法与目录示例竞争,但我希望这有所帮助。
检验这个问题,这也是很有用的,其中公认的答案以通俗易懂的方式演示了递归函数的工作原理。即使OP的问题与Java有关,但概念是相同的,
一个示例是在给定目录的任何子目录中打印每个文件(如果这些目录中没有符号链接,则可能以某种方式破坏该功能)。打印所有文件的伪代码如下所示:
function printAllFiles($dir) {
foreach (getAllDirectories($dir) as $f) {
printAllFiles($f); // here is the recursive call
}
foreach (getAllFiles($dir) as $f) {
echo $f;
}
}
这个想法是先打印所有子目录,然后再打印当前目录的文件。这个想法适用于所有子目录,这就是为所有子目录递归调用此函数的原因。
如果要尝试此示例,则必须检查特殊目录.
和..
,否则您将printAllFiles(".")
始终无法进行调用。此外,您必须检查打印内容和当前的工作目录是什么(见opendir()
,getcwd()
...)。
递归是重复的。就像从内部调用自身的函数一样。让我通过一个伪示例进行演示:
想象一下,您与好友一起喝啤酒,但是如果您在午夜之前不回家,您的妻子将使您下地狱。为此,让我们创建orderAndDrinkBeer($ time)函数,其中$ time是午夜减去您喝完当前饮料并回家所需的时间。
因此,到达酒吧后,您订购了第一杯啤酒并开始饮用:
$timeToGoHome = '23'; // Let's give ourselves an hour for last call and getting home
function orderAndDrinkBeer($timeToGoHome) { // Let's create the function that's going to call itself.
$beer = New Beer(); // Let's grab ourselves a new beer
$currentTime = date('G'); // Current hour in 24-hour format
while ($beer->status != 'empty') { // Time to commence the drinking loop
$beer->drink(); // Take a sip or two of the beer(or chug if that's your preference)
}
// Now we're out of the drinking loop and ready for a new beer
if ($currentTime < $timeToGoHome) { // BUT only if we got the time
orderAndDrinkBeer($timeToGoHome); // So we make the function call itself again!
} else { // Aw, snap! It is time :S
break; // Let's go home :(
}
}
现在,我们只是希望您不能喝足够多的啤酒来陶醉,以至于妻子不准时回家就让您睡在沙发上-.-
但是,是的,这几乎就是递归进行的方式。
它是一个自我调用的函数。它对于遍历某些重复出现的数据结构(例如树)很有用。HTML DOM是一个经典示例。
javascript中树结构的示例以及“遍历”树的递归函数。
1
/ \
2 3
/ \
4 5
-
var tree = {
id: 1,
left: {
id: 2,
left: null,
right: null
},
right: {
id: 3,
left: {
id: 4,
left: null,
right: null
},
right: {
id: 5,
left: null,
right: null
}
}
};
为了遍历树,我们反复调用相同的函数,将当前节点的子节点传递给相同的函数。然后,我们再次调用该函数,首先在左侧节点上,然后在右侧上。
在此示例中,我们将获得树的最大深度
var depth = 0;
function walkTree(node, i) {
//Increment our depth counter and check
i++;
if (i > depth) depth = i;
//call this function again for each of the branch nodes (recursion!)
if (node.left != null) walkTree(node.left, i);
if (node.right != null) walkTree(node.right, i);
//Decrement our depth counter before going back up the call stack
i--;
}
最后我们调用函数
alert('Tree depth:' + walkTree(tree, 0));
理解递归的一种好方法是在运行时逐步遍历代码。
很简单,当一个函数调用自己以完成一段不确定的有限时间的任务时。我自己的代码中的一个示例,该函数用于填充多级类别树
函数category_tree($ parent = 0,$ sep ='') { $ q =“选择ID,类别中的名称,其中parent_id =”。$ parent; $ rs = mysql_query($ q); while($ rd = mysql_fetch_object($ rs)) { echo('id。'“>'。$ sep。$ rd-> name。''); category_tree($ rd-> id,$ sep .'--'); } }
当我了解自己的时候,我已经找到了最好的解释:http : //www.elated.com/articles/php-recursive-functions/
这是因为一件事:
该函数在调用时在内存中创建(创建新实例)
因此,递归函数不会调用自己,而是调用其他实例-因此它不是内存中的一个函数在发挥作用。它在内存中的几个实例正在返回自身一些值-例如,当函数a调用函数b时,此行为是相同的。您有两个实例,以及递归函数是否称为自身的新实例。
尝试在纸上用实例绘制内存-这将是有意义的。
基本上是这样。它会不断自我调用直到完成
void print_folder(string root)
{
Console.WriteLine(root);
foreach(var folder in Directory.GetDirectories(root))
{
print_folder(folder);
}
}
也适用于循环!
void pretend_loop(int c)
{
if(c==0) return;
print "hi";
pretend_loop(c-);
}
您也可以尝试谷歌搜索。注意“您的意思是您的意思”(单击它...)。http://www.google.com/search?q=recursion&spell=1
这是一个实际的例子(已经有好几个例子了)。我只想添加一个对几乎所有开发人员都有用的功能。
在某个时候,开发人员将需要解析对象,以及API或某种类型的对象或数组的响应。
最初调用此函数来分析可能仅包含参数的对象,但是如果该对象还包含其他对象或数组怎么办?这将需要解决,并且在大多数情况下,基本功能已经做到了,因此该功能只是再次调用自身(在确认键或值是对象还是数组之后)并解析此新对象或数组。最终返回的是一个字符串,该字符串可以在一行上自行创建每个参数以提高可读性,但是您可以轻松地将值记录到日志文件中或插入到DB或其他任何东西中。
我添加了$prefix
参数以使用父元素来帮助描述end变量,以便我们可以看到该值的含义。它不包含null值之类的东西,但是可以通过此示例进行修改。
如果有对象:
$apiReturn = new stdClass();
$apiReturn->shippingInfo = new stdClass();
$apiReturn->shippingInfo->fName = "Bill";
$apiReturn->shippingInfo->lName = "Test";
$apiReturn->shippingInfo->address1 = "22 S. Deleware St.";
$apiReturn->shippingInfo->city = "Chandler";
$apiReturn->shippingInfo->state = "AZ";
$apiReturn->shippingInfo->zip = 85225;
$apiReturn->phone = "602-312-4455";
$apiReturn->transactionDetails = array(
"totalAmt" => "100.00",
"currency" => "USD",
"tax" => "5.00",
"shipping" => "5.00"
);
$apiReturn->item = new stdClass();
$apiReturn->item->name = "T-shirt";
$apiReturn->item->color = "blue";
$apiReturn->item->itemQty = 1;
并使用:
var_dump($apiReturn);
它会返回:
object(stdClass)#1(4){[“” shippingInfo“] => object(stdClass)#2(6){[” fName“] => string(4)” Bill“ [” lName“] => string( 4)“ Test” [“ address1”] =>字符串(18)“ 22 S. Deleware St.” [“ city”] =>字符串(8)“ Chandler” [“ state”] =>字符串(2)“ AZ” [“ zip”] => int(85225)} [“ phone”] =>字符串(12 )“ 602-312-4455” [“ transactionDetails”] => array(4){[“” totalAmt“] => string(6)” 100.00“ [” currency“] => string(3)” USD“ [”税“] => string(4)” 5.00“ [” shipping“] => string(4)” 5.00“} [” item“] => object(stdClass)#3(3){[” name“] = >字符串(7)“ T恤” [“颜色”] =>
这是将其解析为每个参数都带有换行符的字符串的代码:
function parseObj($obj, $prefix = ''){
$stringRtrn = '';
foreach($obj as $key=>$value){
if($prefix){
switch ($key) {
case is_array($key):
foreach($key as $k=>$v){
$stringRtrn .= parseObj($key, $obj);
}
break;
case is_object($key):
$stringRtrn .= parseObj($key, $obj);
break;
default:
switch ($value) {
case is_array($value):
$stringRtrn .= parseObj($value, $key);
break;
case is_object($value):
$stringRtrn .= parseObj($value, $key);
break;
default:
$stringRtrn .= $prefix ."_". $key ." = ". $value ."<br>";
break;
}
break;
}
} else { // end if($prefix)
switch($key){
case is_array($key):
$stringRtrn .= parseObj($key, $obj);
break;
case is_object($key):
$stringRtrn .= parseObj($key, $obj);
break;
default:
switch ($value) {
case is_array($value):
$stringRtrn .= parseObj($value, $key);
break;
case is_object($value):
$stringRtrn .= parseObj($value, $key);
break;
default:
$stringRtrn .= $key ." = ". $value ."<br>";
break;
} // end inner switch
} // end outer switch
} // end else
} // end foreach($obj as $key=>$value)
return $stringRtrn;
} // END parseObj()
这将返回对象,如下所示:
shippingInfo_fName = Bill
shippingInfo_lName = Test
shippingInfo_address1 = 22 S. Deleware St.
shippingInfo_city = Chandler
shippingInfo_state = AZ
shippingInfo_zip = 85225
phone = 602-312-4455
transactionDetails_totalAmt = 100.00
transactionDetails_currency = USD
transactionDetails_tax = 5.00
transactionDetails_shipping = 5.00
item_name = T-shirt
item_color = blue
item_itemQty = 1
为了避免与混淆,我做了嵌套的switch语句if . . . ifelse . . . else
,但是它几乎一样长。如果有帮助,请询问是否有条件,我可以将它们粘贴给需要的人。
遍历目录树是一个很好的例子。您可以执行类似的操作来处理数组。这是一个非常简单的递归函数,它仅处理一个字符串,一个简单的字符串数组或任何深度的字符串嵌套数组,将字符串中的“ hello”的实例替换为字符串中的“再见”或该数组的值或任何其他值子数组:
function replaceHello($a) {
if (! is_array($a)) {
$a = str_replace('hello', 'goodbye', $a);
} else {
foreach($a as $key => $value) {
$a[$key] = replaceHello($value);
}
}
return $a
}
它知道何时退出,因为在某些时候,它正在处理的“事物”不是数组。例如,如果调用replaceHello('hello'),它将返回“再见”。如果您向它发送一个字符串数组,尽管它将为该数组的每个成员调用一次自身,然后返回处理后的数组。
如果您在Anthony Forloney的示例中添加某个值(例如“ 1”),那么一切将变得很清楚:
function fact(1) {
if (1 === 0) { // our base case
return 1;
}
else {
return 1 * fact(1-1); // <--calling itself.
}
}
原版的:
function fact($n) {
if ($n === 0) { // our base case
return 1;
}
else {
return $n * fact($n-1); // <--calling itself.
}
}
这是带有递归的阶乘的非常简单的示例:
阶乘是一个非常简单的数学概念。他们写成5!这意味着5 * 4 * 3 * 2 *1。所以6!是720和4!是24。
function factorial($number) {
if ($number < 2) {
return 1;
} else {
return ($number * factorial($number-1));
}
}
希望这对您有用。:)
它是一个简单的示例递归(Y)
<?php
function factorial($y,$x) {
if ($y < $x) {
echo $y;
} else {
echo $x;
factorial($y,$x+1);
}
}
$y=10;
$x=0;
factorial($y,$x);
?>
用于Kaprekar常数的递归
function KaprekarsConstant($num, $count = 1) {
$input = str_split($num);
sort($input);
$ascendingInput = implode($input);
$descendingInput = implode(array_reverse($input));
$result = $ascendingInput > $descendingInput
? $ascendingInput - $descendingInput
: $descendingInput - $ascendingInput;
if ($result != 6174) {
return KaprekarsConstant(sprintf('%04d', $result), $count + 1);
}
return $count;
}
该函数将继续使用计算结果调用自身,直到达到Kaprekars常数为止,该常数将返回进行计算的次数。
/ edit对于不了解Kaprekars Constant的任何人,它需要输入4位数字,至少要包含两个不同的数字。