在scala中,以下两个功能的作用完全相同:
<!doctype html>
<html ng-app="myApp">
<body ng-controller="appController as vm">
<p ng-if="$rootScope.timeoutFlag">Button Clicked</p>
<button id="clickmeBtn">Click me!</button>
<script src="node_modules/angular/angular.min.js"></script>
<script src="scripts/app.controller.js"></script>
<script src="scripts/app.service.js"></script>
</body>
</html>
但是@tailrec仅在第二种情况下有效,在第一种情况下,它将生成以下错误:
错误:无法优化@tailrec注释的方法fn:它包含一个 递归调用不在尾部位置 Option(str).filter(_。nonEmpty).flatMap {v =>
为什么给出此错误?以及为什么这两个代码生成不同种类的JVM字节码
答案 0 :(得分:4)
要使fn
是尾递归的,递归调用必须是函数中的最后一个动作。如果将fn
传递给flatMap
之类的另一个函数,则另一个函数在调用fn
之后可以自由执行其他操作,因此编译器无法确定它是否为尾递归。>
在某些情况下,编译器可以检测到调用fn
是另一个函数中的最后一个动作,但通常情况下不是。而且这将依赖于其他功能的特定实现,因此如果更改了其他功能,tailrec
批注可能会失效,这是不希望的依赖项。
答案 1 :(得分:2)
专门针对最后一个问题:
为什么这两个代码会生成不同种类的JVM字节码
因为在JVM上,所以不能保证运行时包含Option
类的JAR与编译时相同。这样做很好,因为否则,即使是次要版本的库(包括标准Java和Scala库)也将不兼容,并且您需要所有依赖项都使用其共同依赖项的相同次要版本。
如果该类没有合适的flatMap
方法,您将得到AbstractMethodError
,但否则Scala的语义要求必须调用其flatMap
方法。因此,编译器必须发出字节码才能实际调用该方法。
Kotlin通过使用inline
functions和Scala 3 will support them too来解决此问题,但我不知道在这种情况下是否会使用它们。
答案 2 :(得分:1)
请考虑以下内容:
List('a', 'b').flatMap(List(_,'g')) //res0: List[Char] = List(a, g, b, g)
我似乎很明显flatMap()
正在进行一些内部后处理以达到该结果。 List('a','g')
与List('b','g')
怎么结合?