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)

相关推荐

  • 和的立方,自然数立方和公式的推导

    技术和的立方,自然数立方和公式的推导平方和的推导利用立方公式和的立方:(n+1)³-n³=3n²+3n+1 ①记Sn=1²+2²+.+n², Tn=1+2+..+n=n(n+1)/2
    对①式从1

    生活 2021年10月29日
  • css如何设置div的最大高度

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

    攻略 2021年11月9日
  • 琰字寓意,“福”字倒贴的寓意是什么

    技术琰字寓意,“福”字倒贴的寓意是什么老北京人在过春节的时候,家家户户都会在除夕夜贴上春联,以此寄托对来年美好生活的期盼和对家人的祝福琰字寓意。在贴春联的时候,并不是所有的地方都会贴上长条的、内容复杂的春联。有些地方如屋

    生活 2021年10月22日
  • Python语法技巧有哪些

    技术Python语法技巧有哪些这篇文章主要讲解了“Python语法技巧有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Python语法技巧有哪些”吧!1. for -

    攻略 2021年11月20日
  • 手机怎么拍一寸照片,自拍照如何做成一寸证件照

    技术手机怎么拍一寸照片,自拍照如何做成一寸证件照第一步,先打开手机上面的微信,点击下方的“发现”即可手机怎么拍一寸照片。在微信“发现”页面点击“小程序”,并且在输入框内输入证件照。此时,点击手机页面的证件照生成器进入就可

    生活 2021年10月30日
  • html中em是什么单位(em值是什么意思)

    技术css3中em指的是什么单位小编给大家分享一下css3中em指的是什么单位,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧! 在css3中,em是一个相对长度单位,相对于当前

    攻略 2021年12月19日