Scala尾递归的跟踪调用及局限方法是什么

技术Scala尾递归的跟踪调用及局限方法是什么这篇文章主要讲解了“Scala尾递归的跟踪调用及局限方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Scala尾递归

本文主要讲解“Scala尾部递归的跟踪调用和限制方法是什么”,简单明了,易学易懂。请跟随边肖的思路一起学习和学习“Scala尾部递归的跟踪调用和限制方法是什么”!

如果你想把更新var的while循环变成只使用val的更实用的风格,有时你可以使用递归。以下示例是一个递归函数,它通过不断改进猜测数来逼近一个值:

函数如DEF Approximate(猜测: double): double=if(is good stow(猜测))猜测估计(improve(猜测)),在适当实现is good stow和improve的情况下,经常用于发现问题。如果希望近似函数执行得更快,您可能会尝试使用while循环来编写,以加快速度,例如:

defapproximatelop(initial guess : double): double={ varguess=initial guess while(!够好吗(猜)猜=提高(猜)猜}两个近似版本哪个更好?就简单性和避免var而言,* * *,函数类型胜出。但是有没有可能指令性方法会更有效呢?其实如果我们测量一下执行时间,就会发现它们几乎一模一样!这可能令人惊讶,因为递归调用似乎比简单地从循环的末尾跳到循环的开头花费的时间更长。

然而,在上面的近似例子中,Scala编译器可以应用一个重要的优化。请注意,递归调用是由近似函数体执行的* * *事情。像近似的,当他们* * *调用自己的函数时,就叫做尾递归。Scala编译器在检测到结束递归后,用新的值更新函数参数,然后用跳转回函数的开头来替换它。

在道德上,你不应该羞于使用递归算法来解决你的问题。递归通常是比基于循环更优雅和简洁的方案。如果该方案是尾部递归,则不需要支付任何运行时开销。

跟踪尾递归函数

尾部递归函数不会为每次调用创建新的堆栈帧;对的所有调用都将在一个框架中执行。这可能会让检查程序堆栈跟踪并失败的程序员感到惊讶。例如,此函数在多次调用自身后引发异常:

def boom(x : int): int=if(x==0)thrownew exception(' boom!')else BOM(x-1)1这个函数不是尾部递归,因为增量操作是在递归调用之后执行的。如果你执行它,你会得到你所期望的:

Scala BOM(3)Java . lang . exception : boom!At.boom(控制台:5) at.boom(控制台33606) at.boom(控制台:6) at.boom(控制台33606) at.init(控制台:6).如果修改boom now使其成为尾部递归:

defbang(x : int): int=if(x==0)thrownew exception(' bang!')elsebang(x1)您将获得:

scalabang(5)Java . lang . exception : bang!at.bang(控制台:5)

nbsp; at .< init>(< console>:6)  ...

这回,你仅看到了bang的一个堆栈框架。或许你会认为bang在调用自己之前就崩溃了,但这不是事实。如果你认为你会在看到堆栈跟踪时被尾调用优化搞糊涂,你可以用开关项关掉它:

-g:notailcalls

把这个参数传给scala的shell或者scalac编译器。定义了这个选项,你就能得到一个长长的堆栈跟踪了:

scala> bang(5)  java.lang.Exception: bang!   at .bang(< console>:5)   at .bang(< console>:5)   at .bang(< console>:5)   at .bang(< console>:5)   at .bang(< console>:5)   at .bang(< console>:5)   at .< init>(< console>:6)  ...

尾调用优化

approximate的编译后代码实质上与approximateLoop的编译后代码相同。两个函数编译后都是同样的事三个Java字节码指令。如果你看一下Scala编译器对尾递归方法,approximate,产生的字节码,你会看到尽管isGoodEnough和improve都被方法体调用,approximate却没有。Scala编译器优化了递归调用:

public double approximate(double);   Code:    0: aload_0    1: astore_3    2: aload_0    3: dload_1    4: invokevirtual #24; //Method isGoodEnough:(D)Z    7: ifeq 12   10: dload_1    11: dreturn    12: aload_0    13: dload_1    14: invokevirtual #27; //Method improve:(D)D    17: dstore_1    18: goto 2

尾递归的局限

Scala里尾递归的使用局限很大,因为JVM指令集使实现更加先进的尾递归形式变得很困难。Scala仅优化了直接递归调用使其返回同一个函数。如果递归是间接的,就像在下面的例子里两个互相递归的函数,就没有优化的可能性了:

def isEven(x: Int): Boolean =   if (x == 0) true else isOdd(x - 1)  def isOdd(x: Int): Boolean =   if (x == 0) false else isEven(x - 1)

同样如果***一个调用是一个函数值你也不能获得尾调用优化。请考虑下列递归代码的实例:

val funValue = nestedFun _  def nestedFun(x: Int) {   if (x != 0) { println(x); funValue(x - 1) }  }

funValue变量指向一个实质是包装了nestedFun的调用的函数值。当你把这个函数值应用到参数上,它会转向把nestedFun应用到同一个参数,并返回结果。因此你或许希望Scala编译器能执行尾调用优化,但在这个例子里做不到。因此,尾调用优化受限于方法或嵌套函数在***一个操作调用本身,而没有转到某个函数值或什么其它的中间函数的情况。

感谢各位的阅读,以上就是“Scala尾递归的跟踪调用及局限方法是什么”的内容了,经过本文的学习后,相信大家对Scala尾递归的跟踪调用及局限方法是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/138729.html

(0)

相关推荐

  • 用java技术实现网上聊天系统(java聊天室系统整体设计)

    技术基于Java怎么实现简易的局域网对话系统基于Java怎么实现简易的局域网对话系统,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。先说一下写的确实比较一般,

    攻略 2021年12月20日
  • Scala简化代码的方法是什么

    技术Scala简化代码的方法是什么本篇内容介绍了“Scala简化代码的方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有

    攻略 2021年12月10日
  • 特此汇报,煤矿事故向上级汇报应该是谁汇

    技术特此汇报,煤矿事故向上级汇报应该是谁汇根据《矿山安全法》(2009年修订)第二十条第二款关于“矿长对本企业的安全生产工作负责”之规定,发生事故的煤矿企业的矿长应负责及时报告该煤矿的主管部门及其所在地的安监部门特此汇报

    生活 2021年10月21日
  • 怎么实现MySQL中的半同步复制

    技术怎么实现MySQL中的半同步复制这篇文章给大家介绍怎么实现MySQL中的半同步复制,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。关于MySQL的复制架构,大体有下面三种方式,异步,全同步复制,半

    攻略 2021年11月16日
  • centos7 k8s集群(centos7安装k8s集群)

    技术centos7系统部署k8s集群的示例分析centos7系统部署k8s集群的示例分析,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1 版

    攻略 2021年12月15日
  • 如何用Python爬虫抓取代理IP

    技术如何用Python爬虫抓取代理IP本篇文章为大家展示了如何用Python爬虫抓取代理IP,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。不知道大家在访问网站的时候有没有遇到过这

    攻略 2021年10月28日