现实世界的recursion示例
除了深度优先search(DFS)之外,recursion方法是自然的解决scheme, 现实世界的问题是什么?
(我不认为是河内塔 , 斐波纳契数 ,还是现实世界的问题,在我看来都是有点做作的)。
这里有很多的例子,但是你想要一个真实世界的例子,所以有一些想法,这可能是我可以提供的最好的例子:
你会发现一个人感染了一个非致死性的感染性疾病,并迅速修复(A型),除了每5个人中有一个(我们称之为B型)症状,只是作为一个吊具。
当B型感染多种A型时,这会造成非常恼人的波动。
你的任务是追查所有的B型和免疫他们停止疾病的骨干。 不幸的是,你不能在全民范围内治愈所有人,因为typeA的人也对B型的治疗方法致命的过敏。
这样做的方式将是社会发现,给予感染者(typesA),在上周select所有联系人,将每个联系人标记为堆。 当你testing一个人被感染时,将他们添加到“跟进”队列中。 当一个人是B型的时候,把他们加到头上的“跟进”上(因为你想快速停下来)。
在处理特定人员后,从队列的前面select人员,并根据需要进行免疫接种。 获取之前未访问过的所有联系人,然后testing是否受到感染。
重复,直到感染者的队列变为0,然后等待另一个爆发。
(好吧,这是有点迭代的,但它是一个解决recursion问题的迭代方法,在这种情况下,首先遍历人口基数,试图发现可能的问题path,而且迭代解决scheme通常更快,更有效,我强迫性地去除遍地的recursion,所以它变得本能。
一个现实世界的recursion示例
如何在文件系统中涉及目录结构的任何事情。 recursion查找文件,删除文件,创build目录等
这是一个Java实现,recursion地打印出一个目录及其子目录的内容。
import java.io.File; public class DirectoryContentAnalyserOne implements DirectoryContentAnalyser { private static StringBuilder indentation = new StringBuilder(); public static void main (String args [] ){ // Here you pass the path to the directory to be scanned getDirectoryContent("C:\\DirOne\\DirTwo\\AndSoOn"); } private static void getDirectoryContent(String filePath) { File currentDirOrFile = new File(filePath); if ( !currentDirOrFile.exists() ){ return; } else if ( currentDirOrFile.isFile() ){ System.out.println(indentation + currentDirOrFile.getName()); return; } else{ System.out.println("\n" + indentation + "|_" +currentDirOrFile.getName()); indentation.append(" "); for ( String currentFileOrDirName : currentDirOrFile.list()){ getPrivateDirectoryContent(currentDirOrFile + "\\" + currentFileOrDirName); } if (indentation.length() - 3 > 3 ){ indentation.delete(indentation.length() - 3, indentation.length()); } } } }
快速 sorting , 合并sorting ,以及大多数其他的N日志types。
recursion通常用于Backtrackingalgorithm的实现 。 对于这个“真实世界”的应用, 数独解算器怎么样?
马特·迪拉德的例子很好。 更一般地说,树的任何行走通常都可以很容易地通过recursion来处理。 例如,编译parsing树,遍历XML或HTML等
当然,那里的许多编译器大量使用recursion。 计算机语言本身就是recursion的(也就是说,你可以在if语句中embeddedif语句等等)。
recursion是合适的,只要问题可以通过将其分解成子问题来解决,可以使用相同的algorithm来解决它们。 有关树和sorting列表的algorithm是非常合适的。 计算几何(以及3D游戏)中的许多问题可以使用二元空间分割 (BSP)树, 脂肪细分或将世界划分为子部分的其他方式recursion地解决。
当您试图保证algorithm的正确性时,recursion也是适当的。 给定一个函数,它接受不变的input并返回一个结果,这个结果是input上的recursion调用和非recursion调用的结合,通常使用math归纳certificate函数是正确的(或不是)是正确的。 使用迭代函数或input可能会发生变化通常很难做到这一点。 这在处理财务计算和其他正确性非常重要的应用程序时非常有用。
禁用/设置只读容器控件中的所有子控件。 我需要这样做,因为一些儿童控制器本身就是容器。
public static void SetReadOnly(Control ctrl, bool readOnly) { //set the control read only SetControlReadOnly(ctrl, readOnly); if (ctrl.Controls != null && ctrl.Controls.Count > 0) { //recursively loop through all child controls foreach (Control c in ctrl.Controls) SetReadOnly(c, readOnly); } }
从SICP着名的评估/申请周期
这里是eval的定义:
(define (eval exp env) (cond ((self-evaluating? exp) exp) ((variable? exp) (lookup-variable-value exp env)) ((quoted? exp) (text-of-quotation exp)) ((assignment? exp) (eval-assignment exp env)) ((definition? exp) (eval-definition exp env)) ((if? exp) (eval-if exp env)) ((lambda? exp) (make-procedure (lambda-parameters exp) (lambda-body exp) env)) ((begin? exp) (eval-sequence (begin-actions exp) env)) ((cond? exp) (eval (cond->if exp) env)) ((application? exp) (apply (eval (operator exp) env) (list-of-values (operands exp) env))) (else (error "Unknown expression type - EVAL" exp))))
这里是apply的定义:
(define (apply procedure arguments) (cond ((primitive-procedure? procedure) (apply-primitive-procedure procedure arguments)) ((compound-procedure? procedure) (eval-sequence (procedure-body procedure) (extend-environment (procedure-parameters procedure) arguments (procedure-environment procedure)))) (else (error "Unknown procedure type - APPLY" procedure))))
这里是eval-sequence的定义:
(define (eval-sequence exps env) (cond ((last-exp? exps) (eval (first-exp exps) env)) (else (eval (first-exp exps) env) (eval-sequence (rest-exps exps) env))))
eval
– > apply
– > eval-sequence
– > eval
在游戏开发(和其他类似领域)中,recursion用于像BSP树一样的碰撞检测。
人们经常使用recursion方法对文件堆栈进行分类。 例如,假设您正在对100个文档进行sorting,并在文档上包含名称。 先把文件放在第一个字母的堆中,然后对每一堆进行分类。
在字典中查找单词通常是通过类似二进制search的技术来执行的,该技术是recursion的。
在组织中,老板经常给部门主pipe命令,而部门主pipe又向pipe理者发出命令等等。
parsing器和编译器可以用recursion下降的方法编写。 不是最好的方法,因为像lex / yacc这样的工具可以生成更快更高效的parsing器,但是从概念上来说简单易用,所以它们仍然很常见。
我最近得到的真实世界需求:
要求A:彻底理解要求A后实现此function
我有一个系统在几个地方使用纯尾部recursion来模拟状态机。
recursion的一些很好的例子可以在函数式编程语言中find。 在函数式编程语言( Erlang , Haskell , ML / OCaml / F#等)中,有任何列表处理使用recursion是很常见的。
当处理典型的命令式OOP风格的语言列表时,将列表实现为链表([item1 – > item2 – > item3 – > item4])是非常常见的。 然而,在一些函数式编程语言中,你会发现列表本身是recursion地实现的,列表的“首部”指向列表中的第一个项目,“尾部”指向包含其余项目的列表[item1 – > [item2 – > [item3 – > [item4 – > []]]]])。 我认为这很有创意。
列表处理与模式匹配相结合,非常强大。 比方说,我想总结一个数字列表:
let rec Sum numbers = match numbers with | [] -> 0 | head::tail -> head + Sum tail
这基本上是说:“如果我们被一个空的列表调用,返回0”(允许我们打破recursion),否则返回head +的值的价值sum与剩下的项目(因此,我们的recursion)调用。
例如,我可能有一个URL列表,我认为要分开每个URL链接到的所有URL,然后减less所有URL的链接总数,以便为页面生成“值”(Google采取PageRank ,你可以find在原来的MapReduce文件中定义)。 您也可以这样做,以便在文档中生成字数。 还有许多许多其他的东西
您可以将此function模式扩展为任何types的MapReduce代码,您可以在其中获取某些内容,对其进行转换,并返回其他内容(无论是其他列表还是列表中的某个zip命令)。
XML,或遍历树的任何东西。 虽然说实话,我从来没有在工作中使用recursion。
recursion适用于可以将其分解(缩小)为较小部分的问题(情况),并且每个部分与原始问题类似。
包含与自身类似的较小部分的事物的好例子是:
- 树结构(一个分支就像一棵树)
- 列表(列表的一部分仍然是一个列表)
- 容器(俄罗斯娃娃)
- 序列(序列的一部分看起来像下一个)
- 对象组(一个子组仍然是一组对象)
recursion是一种将问题分解成越来越小的部分的技术,直到其中一个部分变得足够小而成为一块蛋糕。 当然,在你把它们分解之后,你必须按照正确的顺序将结果“拼凑起来”,形成一个完整的解决scheme。
一些recursion的sortingalgorithm,树行走algorithm,映射/减lessalgorithm,分而治之都是这种技术的例子。
在计算机编程中,大多数基于堆栈的调用返回types语言已经具有内置的recursionfunction:
- 将问题分解成更小的部分==>在原始数据的较小子集上调用它自己)
- 跟踪如何分割==>调用堆栈,
- 结果返回==>基于栈的返回
反馈在分层组织中循环。
顶级老板告诉高层pipe理人员收集公司所有人的反馈意见。
每个主pipe人员收集他/她的直接报告,并告诉他们从他们的直接报告中收集反馈。
并在下线。
没有直接报告的人 – 树中的叶节点 – 给出他们的反馈。
反馈回来了,每个经理加上他/她自己的反馈。
最终,所有的反馈都让它回到了最顶层的老板身上。
这是自然的解决scheme,因为recursion方法允许在每个级别进行过滤 – 重复对照和消除反馈。 最高层的老板可以发一个全球性的电子邮件,让每个员工直接回报给他/她,但是有“你不能处理真相”和“你被解雇”的问题,所以recursion在这里最适合。
假设你正在为一个网站build立一个CMS,在那里你的页面是树形结构,并且说根是主页。
假设您的{user | client | customer | boss}请求您在每个页面上放置一个面包屑path,以显示您在树中的位置。
对于任何给定的页面n,您可能需要recursion到n的父项及其父项等,以recursion方式构build备份到页面树根目录的节点列表。
当然,在这个例子中你每页打几次数据库,所以你可能想使用一些SQL别名,你把页面表作为一个页面,再把页面表作为b,把a.id和b.parent所以你让数据库做recursion连接。 这是一段时间,所以我的语法可能没有帮助。
再次,您可能只想计算一次,并将其与页面logging一起存储,只有在移动页面时才更新。 这可能会更有效率。
无论如何,那是我的$ .02
你有一个N层深的组织树。 有几个节点被检查,你只想扩展到那些已经检查过的节点。
这是我实际编码的东西。 它很好,很容易与recursion。
在我的工作中,我们有一个通用数据结构的系统,可以被描述为一棵树。 这意味着recursion是处理数据的非常有效的技术。
解决它没有recursion将需要很多不必要的代码。 recursion的问题是不容易遵循所发生的事情。 遵循执行stream程,你真的必须集中注意力。 但是,当它的工作代码是优雅和有效的。
财务/物理计算,如复合平均。
- parsingXML文件。
- 多维空间中的高效search 例如, 二维四叉树,三维八叉树,kd树等。
- 分层聚类。
- 想想看,遍历任何层次结构自然适用于recursion。
- C ++中的模板元编程是唯一的方法,在这里没有循环和recursion。
parsingWindows窗体或WebForms(.NET Windows窗体/ ASP.NET )中的控件树。
我所知道的最好的例子是快速sorting ,recursion很简单。 看一眼:
shop.oreilly.com/product/9780596510046.do
http://www.amazon.com/Beautiful-Code-Leading-Programmers-Practice/dp/0596510047
(点击第3章下的第一个字幕:“我写过的最漂亮的代码”)。
电话和有线电视公司维持其布线拓扑结构的模型,实际上是一个大的networking或graphics。 当你想查找所有的父元素或所有子元素时,recursion是遍历这个模型的一种方法。
由于从处理和存储的angular度来看,recursion是昂贵的,所以通常只在拓扑改变并且结果以修改的预先列表格式存储时执行这个步骤。
归纳推理是概念形成的过程,是recursion的。 你的大脑在现实世界中一直都在做。
同上关于编译器的评论。 抽象语法树节点自然适合recursion。 所有的recursion数据结构(链表,树,图等)也更容易用recursion来处理。 我认为,一旦我们失学,我们大多数人都不会因为现实世界的问题types而大量使用recursion,但是把它作为一种select是很好的。
自然数的乘法是一个recursion的真实世界的例子:
To multiply x by y if x is 0 the answer is 0 if x is 1 the answer is y otherwise multiply x - 1 by y, and add x