zoukankan      html  css  js  c++  java
  • 测开之函数进阶· 第1篇《递归函数》

    坚持原创输出,点击蓝字关注我吧

    作者:清菡
    博客:oschina、云+社区、知乎等各大平台都有。

    由于微信公众号推送改为了信息流的形式,防止走丢,请给加个星标 ⭐,你就可以第一时间接收到本公众号的推送!

    目录

    • 一、往期回顾
      • 1.生成器代码详解
      • 2.生成器的三个方法
    • 二、递归函数
      • 1.什么是递归函数
      • 2.递归函数调用原理图
      • 3.递归边界
      • 4.通过递归函数实现的任意数的阶乘
      • 5.这个递归函数的递归临界点在哪?
      • 6.斐波那契数列
    • 三、系列推荐

    一、往期回顾

    1.生成器代码详解

    def gen():
        for i in range(5):
            j = yield i
            print(j)
    
    
    # send:与生成器进行交互
    g = gen()
    
    print(next(g))
    print(next(g))
    

    第一个print(next(g))打印的 0,就是生成器生成的元素。第二个print(next(g))打印的 1 也是生成器生成的元素,None 是print(j)打印的j

    通过生成器获取元素的时候,首先生成器进去的话,当调用生成器获取里面的值,它会从上往下走,走到j = yield i这里,把yield这里的i这个值返回出来,调用完gen()返回一个生成器g

    通过这个生成器next(g)去拿值的时候,然后它从上往下执行代码,走到j = yield i这里,yield相当于把i,通过yield返回出去。

    从生成器里面返回出来,就生成一个数据。生成这个i,到第一个print(next(g))这里,打印的就是i

    第二个print(next(g)),再用next()调用生成器的时候,那么这个生成器会从yield之后继续往下执行。

    通过next()去触发生成器的时候,yield之后是没有内容的,j接收的就是空的,所以打印j的时候,打印出来的是个None

    2.生成器的三个方法

    # 生成器的三个方法: send  close  throw
    
    def gen():
        for i in range(5):
            j = yield i
            print(j)
    
    
    # send:与生成器进行交互
    g = gen()
    
    print(g.send(100))
    print(next(g))
    print(next(g))
    

    运行后报错:

    生成器的send()方法,它运行的时候会从上一个yield结束的地方来进行运行。

    在这里只创建了gen()这个生成器,这个生成器还没有生成过任何数据,这个时候生成器就暂停在函数最开始的地方def gen():这里。

    这里send(100)这个值进去的话,在这里运行,直接运行for i in range(5):这个语句,send(100)生成进去的这个值没有地方接收,所以报错了。

    send()必须在调用了一次next()之后才调用。可以和next()一样,去获取生成器里面的内容。

    2.1next()获取生成器里面的内容:

    # 生成器的三个方法: send  close  throw
    
    def gen():
        for i in range(5):
            j = yield i
            print(j)
    
    
    # send:与生成器进行交互
    g = gen()
    
    print(next(g))
    print(next(g))
    # print(g.send(100))
    
    
    

    2.2send()在调用了一次next()之后调用,获取生成器里面的内容:

    # 生成器的三个方法: send  close  throw
    
    def gen():
        for i in range(5):
            j = yield i
            print(j)
    
    
    # send:与生成器进行交互
    g = gen()
    
    print(next(g))
    print(g.send(100))
    # print(next(g))
    

    yield只能在函数里面用。yield关键字是用在创建生成器的时候,只要函数里面使用了yield关键字,在调用函数的时候,函数不会立马被执行。

    因为这个函数不是简单的函数了,它是个生成器。

    在函数外面,是没办法用yield关键字的。

    2.3close():关闭生成器

    def gen():
        for i in range(5):
            j = yield i
            print(j)
    
    yield 100
    
    # send:与生成器进行交互
    g = gen()
    
    print(next(g))
    # print(next(g))
    # print(g.send(100))
    
    # close:关闭生成器
    g.close()
    print(next(g))
    

    2.4throw()方法:在生成器内部主动引发一个异常。参数:1.异常类型。2.异常信息。

    这个方法可以接收 2 个参数,第一个参数:Exception 异常类型。第二个参数:传入异常的信息。

    Exception 报错:

    g.throw(Exception,"Method throw called!")

    ValueError:

    g.throw(ValueError,"清菡,大事不好,报错了,嘤嘤嘤~")

    二、递归函数

    1.什么是递归函数

    在函数中调用函数自身,我们把这样的函数叫做递归函数。

    2.递归函数调用原理图

    递归函数调用自身的图,图片来自网络

    3.递归边界

    递归边界:退出递归的终止条件。

    def func():
        print('99999')
        func()
    
    func()
    

    在外面调用函数,直接陷入一个死循环。在函数内部调用func()这个函数,又到def func():这里来执行,然后print('99999'),又func()调用。

    不断得自身调用,这样就造成了死循环。

    Pycharm 有个检测机制: 当它内部检测到这个是个无限递归,没有递归临界点的一个递归函数,那么这个时候,它递归多少次之后,会自动给终止了。

    使用递归函数的时候,一定要注意一个点:就是一定要设置递归的边界。递归的边界就是递归函数的终止条件。

    如果你不设置递归边界,那么你定义的递归函数就是个死循环,一直无限得调用自身。

    4.通过递归函数实现的任意数的阶乘

    4.1 什么是阶乘?

    1 的阶乘 1
    2 的阶乘 1 * 2
    3 的阶乘 1 * 2 * 3
    4 的阶乘 1 * 2 * 3 * 4

    递归能实现的,通过循环都能实现。

    Python 中递归用得不多,不太建议使用递归,因为递归不太好用,用递归还不如用循环。

    4.2 怎么去算阶乘呢?

    定义个函数,算任意数的阶乘。传 1,就算 1 的阶乘,传 10 就算 10 的阶乘。

    可以这样做:

    首先要判断下它传进来的这个参数是不是等于 1,如果是等于 1 的话,就直接给它return返回出去。然后,如果它不等于 1 的话,就返回return n * (n-1)*(n-2)

    n 传进来是 1,那应该返回 1;如果传的是 2,应该返回return n * (n-1)

    如果在这里用递归函数,调用func(1)。那么这个时候,这个func(1)调用递归函数。

    这个函数返回的是什么?

    调用这段代码:

        if n == 1:
            return 1
    

    返回的是个 1。

    将代码修改成如下:

    def fun(n):
        if n == 1:
            return 1
        else:
            return n * fun(n-1)
    
    fun(3)
    

    如果是fun(3),3 传进来:

    def fun(n):
        if n == 1:
            return 1
    

    肯定是不成立的。

    else后面的代码return n * fun(n-1)

    这里的 n 是个 3,fun(n-1)就是fun(2),那么就是3 * fun(2)

    这个时候会再次调用自身这个函数:

    这个时候 n 是什么?

    fun(2)的时候 n 是个 2,就是3 *2* fun(1)fun(1)再执行下,出来的结果是个 1。那这里就是个 1,就是3*2*1

    等于 3 的时候,返回的结果就是3*2*1

    4.3 改成fun(4)看看:

    首先 4 进来,n 等于 4,fun(n-1)就是fun(3)。调用fun(3)就相当于再次调用fun(n),就是4 *3* fun(2)

    再次调用fun(2),再进来,前面return n * fun(n-1)这一截得到 2,fun(3-1)得到 2,所以最终得到4*3*2* fun(1)

    fun(1)调用,结果出来就是个 1。就是4*3*2*1

    def fun(n):
        if n == 1:
            return 1
        else:
            return n * fun(n-1) # 4 *3*2*1
    
    fun(4)
    

    5.这个递归函数的递归临界点在哪?

        if n == 1:
            return 1
    

    n=1的时候就不会调用自身了。当满足某个条件,不再调用自身,那么这个就被称为递归临界点。

    例如改成n==-1

     if n == -1:
            return 1
    

    这个时候,这个函数的递归临界点在哪?

    这个递归临界点就是-1

    def fun(n):
        if n == -1:# 递归临界点:当达到递归临界点的时候,就不再调用自身函数的条件
            return 1
        else:
            return n * fun(n-1) # 4 *3*2*1
    
    fun(4)
    

    任何递归函数,它的原理都是一样的。定义一个递归函数,在递归函数里面它其实就是不断得调用自身,然后设置递归函数的时候,一定不能忘了递归条件。

    6.斐波那契数列

    后面的数都是等于前 2 个数相加的结果。

    斐波那契数列的第一个数值是 1,第二个数值也是个 1,第三个数等于前两个数相加的结果(那就是 2),第四个数等于于前两个数相加的结果(那就是 3)。

    [1,1,2,3,5]

    以此类推。

    三、系列推荐


    公众号 「清菡软件测试」 首发,更多原创文章:清菡软件测试 110+原创文章,欢迎关注、交流,禁止第三方擅自转载。

  • 相关阅读:
    opencv(8)直方图操作
    opencv(9)直方图均衡化,对比,匹配
    最近没有更新日记
    dsp 链接命令文件的写法
    sqlserver 的数据库备份 还原安全操作 备忘录
    主板的各种抱错声音
    hibernate 的自动生成工具
    如何学习,如何提出问题,如何解决问题,如何脑筋急转弯
    framework 的 错误提示?
    hack 入侵 142 主机的过程
  • 原文地址:https://www.cnblogs.com/qinghan123/p/14167038.html
Copyright © 2011-2022 走看看