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)

相关推荐

  • 在调用HttpRequest.GetBufferlessInputStream之后不支持此方法或属性。怎么办

    技术在调用HttpRequest.GetBufferlessInputStream之后不支持此方法或属性。怎么办今天就跟大家聊聊有关在调用HttpRequest.GetBufferlessInputStream之后不支持

    攻略 2021年10月20日
  • 网页图片,网页图片多少KB最适合

    技术网页图片,网页图片多少KB最适合网页图片10kb-50kb最适合网页图片。网页一般分两种,如果是网页源文件的话那就得看网页内容和程序的复杂,网页源文件因为都是代码,所以一般也就1KB左右。还有就是说被执行过的网页,也

    生活 2021年10月26日
  • 天然气锅炉怎么操作,家用燃气锅炉的正确使用方法

    技术天然气锅炉怎么操作,家用燃气锅炉的正确使用方法1、即使寒冷的冬季不使用热水/采暖热水器,也为了启动防冻结装置,电源需插座上,或将供暖系统内的水放净,才可关闭电源天然气锅炉怎么操作。2、在使用时,首先要注意压力一定不要

    生活 2021年10月19日
  • html盒子模型由什么组成(html盒子模型具有哪些内容)

    技术html的盒子模型有哪些本篇内容主要讲解“html的盒子模型有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“html的盒子模型有哪些”吧!

    攻略 2021年12月23日
  • ASP.NET Core Kestrel如何部署HTTPS

    技术ASP.NET Core Kestrel如何部署HTTPSASP.NET Core Kestrel如何部署HTTPS,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来

    攻略 2021年11月12日
  • C语言中如何求两个矩阵的乘积

    技术C语言中如何求两个矩阵的乘积这篇文章将为大家详细讲解有关C语言中如何求两个矩阵的乘积,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。分析在数学中,矩阵(Matrix)是

    攻略 2021年11月11日