แอนโนเทชัน tailrec
รับประกันการปรับปรุงประสิทธิภาพ
แอนโนเทชัน tailrec เป็นบทเรียน Scala for Backend Engineering & Functional Programming ฟรีบน CoddyKit นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Scala for Backend Engineering & Functional Programming และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 4 บทเรียน
การเรียกซ้ำแบบหางคืออะไร
การเรียกซ้ำจะอยู่ใน ตำแหน่งหาง เมื่อเป็นการกระทำสุดท้ายของฟังก์ชันอย่างแท้จริง ฟังก์ชันแบบ เรียกซ้ำแบบหาง สามารถปรับให้เป็นลูปได้ โดยใช้เฟรมของสแตกเพียงเฟรมเดียว จึงไม่ทำให้สแตกเต็ม
ตำแหน่งหาง
ใน n * factorial(n-1) การเรียกซ้ำ ไม่ใช่ การกระทำสุดท้าย เพราะการคูณจะเกิดขึ้นหลังจากฟังก์ชันส่งค่ากลับ ส่วนใน gcd(b, a % b) การเรียกนั้น เป็น การกระทำสุดท้าย ดังนั้นเฉพาะกรณีหลังจึงเป็นการเรียกซ้ำแบบหาง
คำอธิบายประกอบ @tailrec
นำเข้า scala.annotation.tailrec และใส่คำอธิบายประกอบให้กับเมธอด จากนั้นคอมไพเลอร์จะ ตรวจสอบ ว่าการเรียกนั้นอยู่ในตำแหน่งหางจริงหรือไม่ และใช้การปรับปรุงประสิทธิภาพ หากไม่ใช่ การคอมไพล์จะล้มเหลว
import scala.annotation.tailrec
object Main {
@tailrec
def countdown(n: Int): Unit = {
if (n >= 0) {
println(n)
countdown(n - 1)
}
}
def main(args: Array[String]): Unit = countdown(3)
}การปรับปรุงที่รับประกันได้
ประโยชน์สำคัญของ @tailrec คือ การรับประกันขณะคอมไพล์ คุณจะได้รับแจ้งทันทีหากฟังก์ชันไม่ปลอดภัยต่อสแตก แทนที่จะมาพบจากข้อผิดพลาดขณะทำงานเมื่อใช้ข้อมูลเข้าขนาดใหญ่
gcd แบบเรียกซ้ำแบบหาง
ขั้นตอนวิธีของยูคลิดเป็นการเรียกซ้ำแบบหางอยู่แล้ว เพราะการเรียกซ้ำเป็นผลลัพธ์ทั้งหมดของเนื้อฟังก์ชัน การใส่คำอธิบายประกอบจะยืนยันข้อเท็จจริงนี้
import scala.annotation.tailrec
object Main {
@tailrec
def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
def main(args: Array[String]): Unit = {
println(gcd(1071, 462))
}
}สิ่งที่ทำให้หลุดจากตำแหน่งหาง
รูปแบบทั่วไปที่ทำให้การเรียกหลุดจากตำแหน่งหาง:
- คำนวณทางคณิตศาสตร์กับผลลัพธ์:
n + f(...) - ห่อด้วยตัวสร้าง:
x :: f(...) - ใช้ผลลัพธ์ภายในบล็อก
try
ตัวอย่างที่ไม่ใช่การเรียกซ้ำแบบหาง
ผลรวมนี้ ไม่ใช่ การเรียกซ้ำแบบหาง เพราะการบวกครอบการเรียกไว้ หากใส่คำอธิบายประกอบ @tailrec จะทำให้เกิดข้อผิดพลาดในการคอมไพล์ (ตัวอย่างนี้ไม่ใส่คำอธิบายประกอบเพื่อให้ทำงานได้)
object Main {
def sum(n: Int): Int =
if (n == 0) 0
else n + sum(n - 1)
def main(args: Array[String]): Unit = {
println(sum(100))
}
}เหตุใดจึงปรับปรุงไม่ได้
เนื่องจาก n + sum(n - 1) ต้องจำค่า n ไว้เพื่อทำการบวกให้เสร็จหลังจากการเรียกส่งค่ากลับ แต่ละระดับจึงต้องมีเฟรมของสแตกเป็นของตนเอง คอมไพเลอร์ไม่สามารถยุบรูปแบบนี้ให้เป็นลูปได้ จึงไม่ใช่การเรียกซ้ำแบบหาง
ลูปขนาดใหญ่แบบเรียกซ้ำแบบหาง
ผลรวมแบบเรียกซ้ำแบบหางที่ใช้ตัวสะสมสามารถทำงานกับข้อมูลเข้าขนาดใหญ่มากได้โดยไม่ทำให้สแตกเต็ม เพราะใช้เฟรมเดิมซ้ำ
import scala.annotation.tailrec
object Main {
@tailrec
def sumTo(n: Int, acc: Long = 0): Long =
if (n == 0) acc else sumTo(n - 1, acc + n)
def main(args: Array[String]): Unit = {
println(sumTo(1000000))
}
}tailrec ต้องเป็น final หรือเมธอดภายใน
เพื่อให้ @tailrec ทำงานได้ เมธอดต้องไม่สามารถถูกเขียนทับได้ กล่าวคือต้องเป็น private, final หรือเป็นเมธอดภายใน/เมธอดซ้อน เมธอดที่เปิดให้เขียนทับอาจถูกแทนที่และทำให้การปรับปรุงประสิทธิภาพใช้ไม่ได้ ดังนั้นคอมไพเลอร์จึงปฏิเสธเมธอดนั้น
ข้อควรระวังของการเรียกซึ่งกันและกัน
@tailrec ปรับปรุงได้เฉพาะฟังก์ชันที่เรียก ตัวเอง เท่านั้น ฟังก์ชันสองฟังก์ชันที่เรียกหากันเอง (การเรียกซึ่งกันและกัน) ไม่สามารถปรับเป็นการเรียกแบบหางโดย JVM ได้โดยตรง กรณีนี้ต้องใช้การทำแทรมโพลีน ซึ่งจะกล่าวถึงในภายหลัง
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจของคุณเกี่ยวกับ @tailrec
สรุปทบทวน
คุณได้เรียนรู้เรื่อง คำอธิบายประกอบ @tailrec:
- การเรียกใน ตำแหน่งหาง สามารถปรับให้เป็นลูปได้
@tailrecรับประกันความปลอดภัยต่อสแตกขณะคอมไพล์- เมธอดต้องเป็น
final,privateหรือเป็นเมธอดภายใน - รองรับเฉพาะการเรียกตัวเอง ไม่รองรับการเรียกซึ่งกันและกัน
เรียนรู้ Scala ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 39
- บทเรียน
- 143
คำถามที่พบบ่อย
บทเรียน “แอนโนเทชัน tailrec” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “แอนโนเทชัน tailrec” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Scala for Backend Engineering & Functional Programming ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “แอนโนเทชัน tailrec”
รับประกันการปรับปรุงประสิทธิภาพ คุณปฏิบัติ Scala for Backend Engineering & Functional Programming ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Scala for Backend Engineering & Functional Programming หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Scala for Backend Engineering & Functional Programming บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 2 จากทั้งหมด 4 บทเรียน
บทเรียน “แอนโนเทชัน tailrec” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Scala for Backend Engineering & Functional Programming นี้ได้ไหม
ได้ บทเรียน Scala for Backend Engineering & Functional Programming ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- พื้นฐานการเรียกซ้ำ
- แอนโนเทชัน tailrec
- รูปแบบตัวสะสม
- แทรมโพลีน