什么是PHP中的RECURSIVE函数?
任何人都可以请解释一个递归函数在PHP(不使用斐波纳契)在外行语言和使用示例? 我正在看一个例子,但斐波那契完全失去了我!
预先感谢您;-)你也经常使用它们在网站开发?
Laymens条款:
递归函数是一个自我调用的函数
更深入一点:
如果函数不断调用自己,它是如何知道何时停止的? 你设立了一个条件,称为基本情况。 基本情况告诉我们递归调用什么时候停止,否则它将无限循环。
对我来说,一个很好的学习例子,因为我有一个强大的数学背景,是因数 。 通过下面的评论,似乎功能函数可能有点太多了,我会把它留在这里,以防万一你想要它。
function fact($n) { if ($n === 0) { // our base case return 1; } else { return $n * fact($n-1); // <--calling itself. } }
关于在Web开发中使用递归函数,我个人不会使用递归调用。 不是说我会认为依赖递归是不好的习惯,但是它们不应该是你的第一选择。 如果使用不当,可能会致命。
虽然我无法与目录示例竞争,但我希望这有所帮助。
(4/20/10)更新:
检查这个问题也是有帮助的,在这个问题上,被接受的答案以外行的方式展示了递归函数是如何工作的。 尽管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,来自category的名字,其中parent_id =”。$ parent; $ RS =的mysql_query($ Q); 而($ RD = mysql_fetch_object($ RS)) { 回波( '编号'。“> '” $ $九月RD->名。'); category_tree($ RD-> ID,$月。''); } }
递归是循环的替代方法,很少会给代码带来更多的清晰或优雅。 Progman的答案给了一个很好的例子,如果他不使用递归,他将被迫跟踪他目前的目录(这被称为状态)递归允许他使用堆栈进行簿记(变量并返回一个方法的地址存储)
标准示例阶乘和斐波那契对理解概念没有用处,因为它们很容易被循环替换。
基本上这个。 它一直在调用自己,直到完成
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-); }
你也可以试着用google搜索。 注意“你的意思是”(点击它…)。 http://www.google.com/search?q=recursion&spell=1
这是一个实际的例子(已经有好几个很好的例子)。 我只是想添加一个对几乎任何开发人员都有用的程序。
在某些情况下,开发人员需要像对待API或某种类型的对象或数组一样来解析对象。
这个函数最初被调用来解析一个可能只包含参数的对象,但是如果对象还包含其他对象或数组呢? 这将需要解决,大部分基本功能已经这样做,所以函数只是自己调用自己(确认键或值是一个对象或数组后)并解析这个新的对象或数组。 最终返回的是一个字符串,它自己为了便于阅读而在一行上创建每个参数,但是您可以轻松地将这些值记录到日志文件中,或者插入到数据库或其他任何内容中。
我添加了$prefix
参数来使用父元素来帮助描述最终变量,以便我们可以看到该值的属性。 它不包括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);
它会返回:
对象(stdClass)#1(4){[“shippingInfo”] => object(stdClass)#2(6){[“fName”] => string(4)“Bill”[“lName”] => string 4)“Test”[“address1”] => string(18)“S. S. Deleware St.” [“city”] => string(8)“Chandler”[“state”] => string(2)“AZ”[“zip”] => int(85225)} [“phone”] => string(12 )“”602-312-4455“[”transactionDetails“] => array(4){[”totalAmt“] => string(6)”100.00“[”currency“] => string(3) (4)“5.00”[“shipping”] => string(4)“5.00”} [“item”] => object(stdClass)#3(3){[“name”] = > string(7)“T-shirt”[“color”] => string(4)“blue”[“itemQty”] => int(1)}}
这里是代码解析到一个字符串换行符为每个参数:
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
混淆if . . . ifelse . . . else
if . . . ifelse . . . else
if . . . ifelse . . . else
,但几乎一样长。 如果有帮助,只要问条件,我可以粘贴给那些需要它的人。
遍历目录树就是一个很好的例子。 你可以做类似的处理一个数组。 这是一个非常简单的递归函数,可以简单地处理一个字符串,一个简单的字符串数组,或者任何深度的字符串的嵌套数组,用字符串中的'goodbye'替换'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); ?>
当我得知自己在这里时,我发现了最好的解释: http : //www.elated.com/articles/php-recursive-functions/
它因为一件事:
被调用的函数在内存中创建(新实例创建)
所以递归函数不是调用它自己 ,而是调用其他实例 – 所以它不是内存中的一个函数。 它的两个实例在内存中返回自己的一些值 – 而这种行为是相同的,例如,函数a正在调用函数b。 你有两个实例,以及如果递归函数称为自己的新实例。
尝试在纸上绘制实例,这是有道理的。