Jawaban:
Dari entri blog " Panggilan ekor, @tailrec, dan trampolin ":
- Di Scala 2.8, Anda juga dapat menggunakan
@tailrec
anotasi baru untuk mendapatkan informasi tentang metode mana yang dioptimalkan.
Anotasi ini memungkinkan Anda menandai metode tertentu yang Anda harap akan dioptimalkan oleh compiler.
Anda kemudian akan mendapatkan peringatan jika tidak dioptimalkan oleh kompiler.- Di Scala 2.7 atau sebelumnya, Anda perlu mengandalkan pengujian manual, atau pemeriksaan bytecode, untuk mengetahui apakah suatu metode telah dioptimalkan.
Contoh:
Anda dapat menambahkan
@tailrec
anotasi sehingga Anda dapat yakin bahwa perubahan Anda telah berhasil.
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)
}
}
Dan itu bekerja dari REPL (contoh dari tip dan trik 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 = {
^
Compiler Scala akan secara otomatis mengoptimalkan metode tail-recursive apa pun. Jika Anda memberi anotasi pada metode yang Anda yakini sebagai rekursif-ekor dengan @tailrec
anotasi tersebut, maka kompilator akan memperingatkan Anda jika metode tersebut sebenarnya bukan rekursif-ekor. Hal ini membuat @tailrec
anotasi menjadi ide yang bagus, baik untuk memastikan bahwa metode saat ini dapat dioptimalkan dan tetap dapat dioptimalkan saat dimodifikasi.
Perhatikan bahwa Scala tidak menganggap metode sebagai tail-recursive jika dapat diganti. Jadi metode tersebut harus pribadi, final, pada objek (sebagai lawan kelas atau sifat), atau di dalam metode lain untuk dioptimalkan.
Anotasinya adalah scala.annotation.tailrec
. Ini memicu kesalahan kompiler jika metode tidak dapat dioptimalkan panggilan ekor, yang terjadi jika:
Ini ditempatkan tepat sebelum def
definisi metode. Ia bekerja di REPL.
Di sini kami mengimpor anotasi, dan mencoba menandai metode sebagai @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 {
^
Ups! Doa terakhir adalah 1.+()
, bukan length()
! Mari kita rumuskan kembali metodenya:
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
Perhatikan bahwa length0
secara otomatis bersifat pribadi karena ditentukan dalam cakupan metode lain.
override
anotasi di Java - kodenya bekerja tanpa itu, tetapi jika Anda meletakkannya di sana, ia memberi tahu Anda jika Anda membuat kesalahan.