Відповіді:
З допису блогу " Хвіст дзвінки, @tailrec і батути ":
- У Scala 2.8 ви також зможете використовувати нову
@tailrec
анотацію, щоб отримати інформацію про оптимізовані методи.
Ця анотація дозволяє позначити конкретні методи, які, як ви сподіваєтесь, оптимізує компілятор.
Потім ви отримаєте попередження, якщо компілятор не оптимізує їх.- У Scala 2.7 або попередній версії вам доведеться покладатися на ручне тестування або перевірку байт-коду, щоб з’ясувати, чи оптимізовано метод.
Приклад:
ви можете додати
@tailrec
анотацію, щоб бути впевненим, що ваші зміни спрацювали.
import scala.annotation.tailrec
class Factorial2 {
def factorial(n: Int): Int = {
@tailrec def factorialAcc(acc: Int, n: Int): Int = {
if (n <= 1) acc
else factorialAcc(n * acc, n - 1)
}
factorialAcc(1, n)
}
}
І це працює з REPL (приклад із підказок та підказок Scala REPL ):
C:\Prog\Scala\tests>scala
Welcome to Scala version 2.8.0.RC5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.
scala> import scala.annotation.tailrec
import scala.annotation.tailrec
scala> class Tails {
| @tailrec def boom(x: Int): Int = {
| if (x == 0) throw new Exception("boom!")
| else boom(x-1)+ 1
| }
| @tailrec def bang(x: Int): Int = {
| if (x == 0) throw new Exception("bang!")
| else bang(x-1)
| }
| }
<console>:9: error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
@tailrec def boom(x: Int): Int = {
^
<console>:13: error: could not optimize @tailrec annotated method: it is neither private nor final so can be overridden
@tailrec def bang(x: Int): Int = {
^
Компілятор Scala автоматично оптимізує будь-який справді рекурсивний метод. Якщо ви анотуєте метод, який, на вашу думку, є хвостово-рекурсивним @tailrec
, то компілятор попередить вас, якщо метод насправді не є рекурсивним. Це робить @tailrec
анотацію гарною ідеєю як для того, щоб гарантувати, що метод в даний час оптимізується, так і що він залишається оптимізованим у міру модифікації.
Зауважте, що Scala не вважає метод рекурсивним, якщо його можна замінити. Таким чином, метод повинен бути приватним, остаточним, для об’єкта (на відміну від класу чи ознаки), або всередині іншого методу, який потрібно оптимізувати.
Анотація є scala.annotation.tailrec
. Це викликає помилку компілятора, якщо метод не може бути оптимізований хвостовим викликом, що трапляється, якщо:
Він розміщується безпосередньо перед def
у визначенні методу. Це працює в REPL.
Тут ми імпортуємо анотацію та намагаємося позначити метод як @tailrec
.
scala> import annotation.tailrec
import annotation.tailrec
scala> @tailrec def length(as: List[_]): Int = as match {
| case Nil => 0
| case head :: tail => 1 + length(tail)
| }
<console>:7: error: could not optimize @tailrec annotated method: it contains a recursive call not in tail position
@tailrec def length(as: List[_]): Int = as match {
^
На жаль! Останній виклик - 1.+()
ні length()
! Давайте переформулюємо метод:
scala> def length(as: List[_]): Int = {
| @tailrec def length0(as: List[_], tally: Int = 0): Int = as match {
| case Nil => tally
| case head :: tail => length0(tail, tally + 1)
| }
| length0(as)
| }
length: (as: List[_])Int
Зверніть увагу, що length0
це автоматично приватне, оскільки це визначено в області іншого методу.
override
щось на зразок анотації в Java - код працює без неї, але якщо ви помістите її туди, він повідомить вам, чи зробили ви помилку.