递归 尾递归

by Michael Olorunnisola

通过Michael Olorunnisola

递归,递归,递归 (Recursion, Recursion, Recursion)

Before I tell you what recursion is, you should read this article:

在我告诉您什么是递归之前,您应该阅读以下文章:

Recursion, Recursion, RecursionBefore I tell you what recursion is, you should read this article:medium.freecodecamp.com

递归,递归,递归 在告诉您什么是 递归 之前,您应该阅读以下文章: medium.freecodecamp.com

Give yourself a pat on the back if you didn’t fall for that one. If you did, no worries — you now know what recursion is!

如果您不喜欢那个,请给自己一个拍子。 如果您这样做了,那就不用担心-您现在知道什么是递归了!

People often joke that in order to understand recursion, you must first understand recursion. — John D. Cook

人们经常开玩笑说,为了了解递归,您必须首先了解递归。 约翰·库克(John D. Cook)

When I first started coding, I thought about recursion all the time. I used to think of it as some magic spell, or some higher order technique that only the best of the best developers used to solve the hardest problems.

当我第一次开始编码时,我一直都在考虑递归。 我曾经将其视为某种魔术,或一些仅由最好的开发人员中的最好的人用来解决最棘手的问题的高级技术。

As it turns out, it’s not magic at all. But good developers do understand it. And great developers understand when it’s best to use it.

事实证明,这根本不是魔术。 但是优秀的开发人员确实了解它。 优秀的开发人员了解何时最佳使用它。

So what exactly is recursion?

那么递归到底是什么呢?

Have you ever practiced something over and over again until the point where you “got it down?” Then you’ve preformed a recursive act.

您是否曾经一遍又一遍地练习某些东西,直到您“忘了它”? 然后,您已经执行了递归操作。

Plainly speaking, you executed a task or series of steps repeatedly until you reached some desired goal. That, my friends, is the essence of recursion.

概括地说,您重复执行了一项任务或一系列步骤,直到达到某个期望的目标。 我的朋友们,那是递归的本质。

In code speak, a recursive function is a function which calls itself.

用代码来讲,递归函数是一个调用自身的函数。

Before we jump into any code, let’s walk through a basic example to understand the structure of recursive functions. Since the piano is near and dear to my heart, we will make a function called practicePiano.

在进入任何代码之前,让我们看一个基本的例子来理解递归函数的结构。 由于钢琴是我亲爱的亲爱的,因此我们将创建一个称为PracticePiano的功能。

Every time this function is called with a person, that person will practice the piano. Since I don’t spend enough time practicing right now, I should get a little practice in.

每次有人呼叫此功能时,该人都会练习钢琴。 由于我现在没有花足够的时间练习,因此我应该进行一些练习。

practicePiano(person){  practiceScales(person);    practiceChords(person);}
practicePiano('Michael');

I’ve called the function above once, so I was able to get one session in, but I definitely need more than one to really get better.

我已经调用了上面的函数一次,因此能够参加一个会议,但要真正变得更好,我肯定需要多个会议。

practicePiano('Michael');practicePiano('Michael');practicePiano('Michael');practicePiano('Michael');practicePiano('Michael');practicePiano('Michael');practicePiano('Michael');practicePiano('Michael');...

This is great and all, but it breaks one of programming’s greatest tenets: Don’t Repeat Yourself (DRY).

固然很好,但它打破了编程的最大宗旨之一:不要重复自己(DRY)。

I was able to get more practice sessions in, but every time I want to practice more, I have to add another call to practicePiano.

我可以参加更多的练习班,但是每次我想练习更多时,我都必须再添加一个练习钢琴的电话。

One way we can solve this problem is by calling the function within itself, so every time I practicePiano, I practice more:

解决这个问题的一种方法是在内部调用函数,因此每次我练习钢琴时,我都会练习更多:

practicePiano(person){  practiceScales(person);    practiceChords(person);
//Recursive magic here!
practicePiano(person);}
//Now we only need one of these!
practicePiano('Michael');

This is awesome! I only have to call it once. The only problem is, once I call this function… I never stop practicing. I never stop until it is literally impossible for me to practice anymore.

这太棒了! 我只需要打一次。 唯一的问题是,一旦调用此函数,我就永远不会停止练习。 我永不停止,直到我再也无法练习为止。

//Our code above would behave as follows:
practiceScales('Michael');    practiceChords('Michael');
//Recursive Call
practiceScales('Michael');    practiceChords('Michael');
//Recursive Call
practiceScales('Michael');    practiceChords('Michael');
//Recursive Call
//..till I can't physically practice anymore

When your computer reaches a similar point where it’s unable to continue it usually returns this error:

当计算机到达无法继续的类似点时,通常会返回此错误:

RangeError: Maximum call stack size exceeded

That is the equivalent of your computer saying, “I’ve run out of space, and had to close up shop.” It has recorded each function call in memory in a stack. But since the calls never stop, the stack gets completely filled and the computer is forced to stop. (This is where the name for the popular website, Stack Overflow, comes from.)

这相当于您的计算机说:“我的空间用完了,不得不关门了。” 它已将每个函数调用记录在堆栈中的内存中。 但是由于调用永不停止,因此堆栈已完全装满,并且计算机被迫停止。 (这是流行网站的名称Stack Overflow的来源。)

So going back to my never ending piano practice, how can we keep my hands from falling off?

那么回到我永无止境的钢琴练习中,我们如何才能防止手掉下来呢?

This is where we see the importance of a term you may have heard before: the base case.

在这里,我们可以看到您之前可能听说过的术语的重要性: 基本案例

In recursive functions the base case is the goal you are trying to achieve or the task you are looking to complete. The base case’s job is to tell your function when it should stop.

在递归函数中,基本情况是您要尝试实现的目标或要完成的任务。 基本案例的工作是告诉您的函数何时应该停止。

In our analogy, that goal can be practicing until I’m tired.

打个比方,这个目标可以一直练习直到我累了。

practicePiano(person){   if (tired(person)){ //When I am finally tired    console.log("Guess you can take a break now...");    return ;  //This will return out of the function and stop the recursive call  }
practiceScales(person);    practiceChords(person);
//Recursive magic here!
practicePiano(person);}
//Now when we call this here...I'll only practice over and over again until I'm tired
practicePiano('Michael');

This is where most developers run into problems. Although our current piano practice analogy is a simplification, it drives home an extremely important point: what if I never got tired of practicing? Then the base case we wrote would not resolve our “Maximum call stack size exceeded” error.

这是大多数开发人员遇到问题的地方。 尽管我们目前的钢琴练习类比只是一种简化,但它带给我一个极其重要的观点:如果我从不厌倦练习,该怎么办? 这样,我们编写的基本案例将无法解决“超出最大调用堆栈大小”错误。

Before we jump straight into coding, it’s important to take the time to reflect on all the possible situations that can — and should — stop our recursive calls.

在我们直接进行编码之前,重要的是要花时间思考所有可能并且应该停止递归调用的情况。

As a developer, you’ll write complex algorithms, which may take variable inputs and use recursion to achieve some goal.

作为开发人员,您将编写复杂的算法,这些算法可能需要可变的输入并使用递归来实现某些目标。

You may develop a base case or multiple base cases that you believe will be reached by your recursive call. But that may not always be the case.

您可以开发一个或多个您认为递归调用可以达到的基本案例。 但这并非总是如此。

Consider the case of my cyborg counterpart:

考虑我的机器人机器人的情况:

practicePiano(person){   if (tired(person)){    console.log("Guess you can take a break now...");    return ;    }
if (handsFallOff(person)){   console.log("Go see a doctor about that");    return ; }
practiceScales(person);    practiceChords(person);
practicePiano(person);}
practicePiano('Cyborg-Michael');
//Cyborg-Michael never gets tired//Nor do his hands ever fall off//Back to being stuck practicing forever...

Given this, it’s important to always make sure you are thorough in developing your base case(s) and certain your function behaves in a way that a base case will always be reached.

鉴于此,重要的是要始终确保开发完基本案例并确保您的函数以始终达到基本案例的方式运行。

In our example, a logical base case would be being able to play some piece like Beethoven’s 5th.

在我们的示例中,一个合乎逻辑的基本案例将是能够演奏一些贝多芬的第五乐。

Refactoring our code, we now have:

重构我们的代码,我们现在有:

practicePiano(person, song){   if (tired(person)){    console.log("Guess you can take a break now...");    return ;    }
if (handsFallOff(person)){   console.log("Go see a doctor about that");    return ; }
if (song(person)){   console.log("Great Job! Time to learn this on the guitar!");    return ; }
practiceScales(person);    practiceChords(person);
practicePiano(person, song);}
practicePiano('Cyborg Michael', BeethovenFifth);
//The Cyborg version of me never gets tired//Nor do my hands ever fall off//But, being a cyborg...I can learn Beethoven's 5th pretty quickly

This is the power of recursive solutions. With a few lines of code, I’m able to achieve some task, for which I may not know how many steps it may take. I may need 100 practice session, whereas my cyborg self will only need 5, but this solution will still work for the both of us.

这就是递归解决方案的力量。 用几行代码,我就能完成一些任务,而我可能不知道它可能要执行多少步骤。 我可能需要进行100次练习,而我的机器人自我只需要5次练习,但此解决方案仍然适用于我们俩。

So to recap, just remember the following:

因此,回顾一下,只需记住以下几点:

  1. Recursion allows you to easily repeat a task to accomplish some goal.递归使您可以轻松地重复执行任务以实现某些目标。
  2. The base case(s) should be thorough enough to allow your recursive function to actually reach a conclusion (and not run forever).基本案例应该足够详尽,以使递归函数真正得出结论(而不是永远运行)。
  3. Recursion helps you keep your code DRY (again, you’ll hear this acronym a lot, so remember that it stands for “Don’t repeat yourself” —oops, I just did!)递归可以帮助您使代码保持DRY(再次,您会听到很多首字母缩写词,因此请记住,它代表“不要重复自己” —哎呀,我就是这样!)

更多递归 (More recursion to come)

We’ll go more in-depth into recursion in my upcoming series on data structures. During this deep dive, we’ll get a look into how you can start to use time complexity analysis and recursion in your everyday code. We’ll also work through various looping methods to understand why it may be better to use one over the other.

在我即将发布的有关数据结构的系列文章中,我们将更深入地介绍递归。 在这次深入学习中,我们将研究如何开始在日常代码中使用时间复杂度分析和递归。 我们还将研究各种循环方法,以了解为什么使用一种优于另一种可能更好。

As an aside, one of the topics we didn’t get a chance to cover here are factorial problems. Factorial problems are where you find recursive solutions applied most often as they require recursively iterating some defined number of times. You can find more specifics regarding solving factorial problems in this awesome article by SitePoint.

顺便说一句,这里我们没有机会讨论的主题之一是析因问题。 阶乘问题是您发现递归解决方案最常用的地方,因为它们需要递归地迭代一定的次数。 您可以在SitePoint的这篇很棒的文章中找到有关解决阶乘问题的更多详细信息。

Here are some additional resources to help out:

这里有一些其他资源可以帮助您:

Khan Academy on Recursion Learn for free about math, art, computer programming, economics, physics, chemistry, biology, medicine, finance…www.khanacademy.orgSparkNotes: What is Recursion?: What is Recursion?A summary of What is Recursion? in 's What is Recursion?. Learn exactly what happened in this chapter, scene, or…www.sparknotes.commybrainishuge/recursion-promptsrecursion-prompts - Repository of prompts to be solved using recursiongithub.com

可汗递归学院 免费学习数学,艺术,计算机编程,经济学,物理学,化学,生物学,医学,金融… www.khanacademy.org SparkNotes:什么是递归?:什么是递归? 什么是递归的摘要? 在“什么是递归?”中。 确切了解本章,场景或…中发生了什么 。www.sparknotes.com mybrainishuge / recursion-prompts 递归提示-使用递归 github.com 可以解决的提示存储库

Also, thanks to Yara Tercero for helping edit this.

另外,还要感谢Yara Tercero的帮助。

翻译自: https://www.freecodecamp.org/news/recursion-recursion-recursion-4db8890a674d/

递归 尾递归

递归 尾递归_递归,递归,递归相关推荐

  1. 递归 尾递归_代码简报:递归,递归,递归

    递归 尾递归 Here are three stories we published this week that are worth your time: 这是我们本周发布的三个值得您关注的故事: ...

  2. 递归 尾递归_什么是尾递归?

    递归 尾递归 Here you will learn about what is tail recursion with example. 在这里,您将通过示例了解什么是尾递归. Tail recur ...

  3. java 递归 尾递归_递归和尾递归

    C允许一个函数调用其本身,这种调用过程被称作递归(recursion). 最简单的递归形式是把递归调用语句放在函数结尾即恰在return语句之前.这种形式被称作尾递归或者结尾递归,因为递归调用出现在函 ...

  4. 循环神经网络 递归神经网络_如何用递归神经网络预测空气污染

    循环神经网络 递归神经网络 After the citizen science project of Curieuze Neuzen, I wanted to learn more about air ...

  5. java递归兔子_兔子问题 —— 递归的应用

    兔子问题.递归 public class Test { /** * 兔子问题 * 斐波那契数列 */ @org.junit.Test public void test2() { int m = 5; ...

  6. python递归和循环的区别_递归与伪递归区别,Python 实现递归与尾递归

    递归函数在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函 数.(1) 递归就是在过程或函数里调用自身.(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出 ...

  7. python 递归 分叉_浅谈Python 递归算法指归

    1. 递归概述 递归( recursion)是一种编程技巧,某些情况下,甚至是无可替代的技巧.递归可以大幅简化代码,看起来非常简洁,但递归设计却非常抽象,不容易掌握.通常,我们都是自上而下的思考问题, ...

  8. 尾递归及快排尾递归优化

    尾递归 概念 如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的.当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归.尾递归 ...

  9. 22_IO_第22天(File、递归)_讲义

    今日内容介绍 1.File 2.递归 xmind:下载地址: 链接:https://pan.baidu.com/s/1Eaj9yP5i0x4PiJsZA4StQg 密码:845a 01IO技术概述 * ...

最新文章

  1. IC/FPGA校招笔试题分析(四)再看Moore状态机实现序列检测器
  2. 【完结】12大深度学习开源框架(caffe,tf,pytorch,mxnet等)快速入门项目
  3. python之高级的文件操作shutil模块
  4. 服务器每秒钟执行命令数量是什么_如何合理的评估上线服务器数量
  5. OpenStack版本比较之Keystone
  6. FFmpeg简介及常见用法
  7. js中邦定事件与解绑支持匿名函数
  8. SSH学习(一)—— 基础概念篇
  9. qq家园纵横四海的源码
  10. 基于51单片机+74LS138译码器+8位共阴数码管时钟设计—按键修改时间
  11. 工作中使用到的单词(软件开发)_20210402备份
  12. 制作传播超级手机病毒嫌犯被抓
  13. 数据库的备份与恢复技术
  14. 【技法操作】UI界面设计,用PS绘制录音页面教程
  15. 渤海银行增收不增利:信用卡等不良率暴涨,李伏安被市场“打脸”
  16. Ubuntu 22.04 中的 .NET 6
  17. Ext_多行文本输入框_Ext.form.TextArea
  18. Mac安装多版本java
  19. 堆排序:大顶堆和小顶堆 + 前K个高频元素
  20. 为什么Math.abs(Integr.MIN_VALUE)==Integer.MIN_VALUE 1

热门文章

  1. 字节缓冲流 BufferedOutputStream java
  2. 类与对象 格式小结 java 1202
  3. 前端开发 表单元素 0229
  4. 零基础自学html5要多久?Web前端学习路线的6点建议
  5. SEO之网站内链优化策略
  6. jQuery跨域调用Web API
  7. 一张图看完成都云栖大会的精彩,请用心感受!
  8. C#枚举类型的常用操作总结
  9. WCF性能优势体现 【转】
  10. 修改eclipse皮肤