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)

相关推荐

  • MAC和树莓派如何实现文件共享和TM备份

    技术MAC和树莓派如何实现文件共享和TM备份这篇文章给大家分享的是有关MAC和树莓派如何实现文件共享和TM备份的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。使用 netatalk 让 Linu

    攻略 2021年11月20日
  • FastDFS

    技术FastDFSFastDFS,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。FastDFS1、具体内容如果现在你的系统之中需要存放大量的图片或者是视频资源

    攻略 2021年11月23日
  • python计算变量间的相关系数(python计算多元变量的相关系数)

    技术Python协方差与相关系数怎么定义本篇内容介绍了“Python协方差与相关系数怎么定义”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅

    攻略 2021年12月21日
  • 12组-Alpha冲刺-4/6

    技术12组-Alpha冲刺-4/6 12组-Alpha冲刺-4/6侯钦凯过去两天完成了哪些任务
    完善UI界面,复习考试展示GitHub当日代码/文档签入记录接下来的计划复习考试,准备答辩还剩下哪些任务博

    礼包 2021年11月15日
  • 对MySQL性能影响关系紧密的配置参数有哪些

    技术对MySQL性能影响关系紧密的配置参数有哪些这篇文章主要介绍对MySQL性能影响关系紧密的配置参数有哪些,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!(一)连接连接通常来自Web 服务器,下面

    攻略 2021年12月8日
  • 如何解决java连接zookeeper很慢的问题

    技术如何解决java连接zookeeper很慢的问题这篇文章主要为大家展示了“如何解决java连接zookeeper很慢的问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如

    攻略 2021年11月11日