แทรมโพลีน
การเรียกซ้ำที่ปลอดภัยต่อสแตก
แทรมโพลีน เป็นบทเรียน Scala for Backend Engineering & Functional Programming ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Scala for Backend Engineering & Functional Programming และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 4 บทเรียน
ข้อจำกัดของ @tailrec
@tailrec ปรับปรุงได้เฉพาะฟังก์ชันที่เรียก ตัวเอง โดยตรงเท่านั้น ไม่สามารถช่วยเรื่อง การเรียกซึ่งกันและกัน (ฟังก์ชันสองฟังก์ชันที่เรียกหากันเอง) ซึ่งยังคงทำให้สแตกเติบโตได้ การทำ แทรมโพลีนช่วยแก้ปัญหานี้
ปัญหาการเรียกซึ่งกันและกัน
พิจารณา isEven และ isOdd ที่นิยามโดยอาศัยกันและกัน เมื่อใช้กับตัวเลขขนาดใหญ่ การทำงานจะทำให้สแตกเต็ม และไม่สามารถใส่ @tailrec ให้ฟังก์ชันใดได้
object Main {
def isEven(n: Int): Boolean = if (n == 0) true else isOdd(n - 1)
def isOdd(n: Int): Boolean = if (n == 0) false else isEven(n - 1)
def main(args: Array[String]): Unit = {
println(isEven(10))
}
}แทรมโพลีนคืออะไร
แทรมโพลีนเปลี่ยนการเรียกซ้ำให้เป็น ข้อมูล แทนที่จะเรียกตัวเอง ฟังก์ชันจะส่งคำอธิบายของขั้นตอนถัดไปกลับมา ลูปควบคุมจะทำขั้นตอนเหล่านี้ซ้ำ ๆ ทำให้สแตกคงที่
TailRec ในไลบรารีมาตรฐาน
Scala มี scala.util.control.TailCalls พร้อมชนิด TailRec ใช้ done(x) สำหรับผลลัพธ์สุดท้าย และใช้ tailcall(...) เพื่อเลื่อนการเรียกถัดไปออกไป
import scala.util.control.TailCalls._
object Main {
def isEven(n: Int): TailRec[Boolean] =
if (n == 0) done(true) else tailcall(isOdd(n - 1))
def isOdd(n: Int): TailRec[Boolean] =
if (n == 0) done(false) else tailcall(isEven(n - 1))
def main(args: Array[String]): Unit = {
println(isEven(100000).result)
}
}done และ tailcall
องค์ประกอบพื้นฐานสองอย่าง:
done(value)ห่อคำตอบสุดท้ายtailcall(expr)เลื่อนการเรียกที่ส่งคืนค่าเป็นTailRecออกไป
การเรียก .result จะทำงานลูปแทรมโพลีนและสร้างค่าออกมา
ความปลอดภัยต่อสแตก
เนื่องจาก tailcall แต่ละครั้งส่งการควบคุมกลับไปยังลูปควบคุมแทนการซ้อนการเรียก Java สแตกของ JVM จึงไม่เติบโตตามความลึกของการเรียกซ้ำ ตัวอย่างข้างต้นรองรับ 100,000 ขั้นตอนได้โดยไม่ทำให้สแตกเต็ม
การทำแทรมโพลีนกับการเรียกตัวเอง
แทรมโพลีนยังใช้กับการเรียกตัวเองแบบลึกทั่วไปได้ เมื่อไม่สามารถใช้ตัวสะสมได้สะดวก ในที่นี้การนับถอยหลังที่ลึกยังคงปลอดภัยต่อสแตก
import scala.util.control.TailCalls._
object Main {
def countDown(n: Int): TailRec[Int] =
if (n == 0) done(0) else tailcall(countDown(n - 1))
def main(args: Array[String]): Unit = {
println(countDown(500000).result)
}
}การรวมผลลัพธ์ด้วย flatMap
TailRec รองรับ map และ flatMap คุณจึงทำงานต่อหลังการเรียกที่เลื่อนออกไปได้ โดยยังคงปลอดภัยต่อสแตก
import scala.util.control.TailCalls._
object Main {
def sum(n: Int): TailRec[Int] =
if (n == 0) done(0)
else tailcall(sum(n - 1)).map(_ + n)
def main(args: Array[String]): Unit = {
println(sum(100000).result)
}
}ลูปควบคุมทำงานอย่างไร
ในเชิงแนวคิด .result จะทำงานเป็นลูป: นำขั้นตอนปัจจุบันมา หากเป็น done ก็ส่งค่ากลับ หากเป็นการเรียกที่เลื่อนออกไป ก็ประเมินหนึ่งชั้นแล้วทำต่อ ทั้งหมดนี้ใช้พื้นที่สแตกคงที่
การทำแทรมโพลีนในไลบรารีเอฟเฟกต์
ไลบรารีอย่าง Cats Effect และ ZIO จะทำแทรมโพลีนให้สาย flatMap ภายในไลบรารี นี่จึงเป็นเหตุผลที่คุณสามารถสร้างโปรแกรมเอฟเฟกต์ที่ซ้อนกันลึกมากได้โดยไม่ทำให้สแตกเต็ม การทำแทรมโพลีนเป็นรากฐานของเอฟเฟกต์เชิงฟังก์ชันที่ปลอดภัยต่อสแตก
ควรใช้แทรมโพลีนเมื่อใด
ใช้การเรียกต่อแบบแทรมโพลีนเมื่อ:
- มีการเรียกซ้ำซึ่งกันและกันที่ไม่สามารถเขียนเป็นฟังก์ชันเรียกซ้ำแบบหางเดียวได้
- การเรียกซ้ำลึกเกินไปสำหรับสแตก และไม่เหมาะที่จะใช้ตัวสะสม
สำหรับการเรียกซ้ำตัวเองแบบง่าย ให้เลือกใช้ @tailrec ร่วมกับตัวสะสมก่อน
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจเกี่ยวกับการเรียกต่อแบบแทรมโพลีน
สรุปทบทวน
คุณได้เรียนรู้เรื่องการเรียกต่อแบบแทรมโพลีน:
- ช่วยให้การเรียกซ้ำซึ่งกันและกันและการเรียกซ้ำที่ลึกมากปลอดภัยต่อสแตก
- ใช้
TailCalls:done(x)และtailcall(...)จากนั้นใช้.result TailRecรองรับmap/flatMap- เป็นพื้นฐานของไลบรารีเอฟเฟกต์ที่ปลอดภัยต่อสแตก
คำถามที่พบบ่อย
บทเรียน “แทรมโพลีน” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “แทรมโพลีน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Scala for Backend Engineering & Functional Programming ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “แทรมโพลีน”
การเรียกซ้ำที่ปลอดภัยต่อสแตก คุณปฏิบัติ Scala for Backend Engineering & Functional Programming ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Scala for Backend Engineering & Functional Programming หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Scala for Backend Engineering & Functional Programming บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “แทรมโพลีน” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Scala for Backend Engineering & Functional Programming นี้ได้ไหม
ได้ บทเรียน Scala for Backend Engineering & Functional Programming ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ