รูปแบบตัวสะสม
เปลี่ยนเป็นการเรียกซ้ำแบบหาง
รูปแบบตัวสะสม เป็นบทเรียน Scala for Backend Engineering & Functional Programming ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Scala for Backend Engineering & Functional Programming และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 4 บทเรียน
รูปแบบตัวสะสม
รูปแบบตัวสะสม เปลี่ยนฟังก์ชันที่ไม่ใช่การเรียกซ้ำแบบหางให้เป็นฟังก์ชันแบบเรียกซ้ำแบบหางได้ คุณเก็บผลลัพธ์บางส่วนไว้ในพารามิเตอร์เพิ่มเติม (หรือ ตัวสะสม) แทนการสร้างผลลัพธ์หลังจากการเรียกส่งค่ากลับ
แนวคิดหลัก
แทนที่จะใช้ n + sum(n-1) ซึ่งทำงานหลังการเรียก ให้คำนวณผลรวมบางส่วนใหม่ ก่อนการเรียกด้วย sum(n-1, acc + n) ตอนนี้การเรียกซ้ำจึงเป็นการกระทำสุดท้าย
ก่อนปรับ: ผลรวมที่ไม่ใช่การเรียกซ้ำแบบหาง
เวอร์ชันตรงไปตรงมานี้ไม่ใช่การเรียกซ้ำแบบหาง เพราะการบวกต้องรอการเรียกซ้ำ
object Main {
def sum(n: Int): Int =
if (n == 0) 0 else n + sum(n - 1)
def main(args: Array[String]): Unit = {
println(sum(50))
}
}หลังปรับ: ผลรวมแบบหางที่ใช้ตัวสะสม
เพิ่มพารามิเตอร์ acc เพื่อเก็บผลรวมที่กำลังคำนวณ การเรียกซ้ำจึงอยู่ในตำแหน่งหางและสามารถปรับปรุงได้
import scala.annotation.tailrec
object Main {
@tailrec
def sum(n: Int, acc: Int = 0): Int =
if (n == 0) acc else sum(n - 1, acc + n)
def main(args: Array[String]): Unit = {
println(sum(50))
}
}แฟกทอเรียลแบบเรียกซ้ำแบบหาง
ใช้การเปลี่ยนรูปแบบเดียวกันกับแฟกทอเรียล โดยคูณค่าเข้าไปในตัวสะสมก่อนเรียกซ้ำ
import scala.annotation.tailrec
object Main {
@tailrec
def factorial(n: Int, acc: Long = 1): Long =
if (n <= 1) acc else factorial(n - 1, acc * n)
def main(args: Array[String]): Unit = {
println(factorial(10))
}
}ซ่อนตัวสะสม
พารามิเตอร์เพิ่มเติมเป็นรายละเอียดการทำงานภายใน ให้ห่อฟังก์ชันผู้ปฏิบัติงานแบบเรียกซ้ำแบบหางไว้ในฟังก์ชันสาธารณะที่สะอาด เพื่อไม่ให้ผู้เรียกต้องเห็น acc
import scala.annotation.tailrec
object Main {
def factorial(n: Int): Long = {
@tailrec
def loop(m: Int, acc: Long): Long =
if (m <= 1) acc else loop(m - 1, acc * m)
loop(n, 1)
}
def main(args: Array[String]): Unit = {
println(factorial(6))
}
}การสะสมลิสต์
รูปแบบนี้ยังใช้สร้างคอลเลกชันได้ด้วย การกลับลำดับแบบเรียกซ้ำแบบหางจะนำสมาชิกตัวหน้าแต่ละตัวไปเติมด้านหน้าของลิสต์ตัวสะสม
import scala.annotation.tailrec
object Main {
def reverse[A](xs: List[A]): List[A] = {
@tailrec
def loop(rem: List[A], acc: List[A]): List[A] = rem match {
case Nil => acc
case h :: t => loop(t, h :: acc)
}
loop(xs, Nil)
}
def main(args: Array[String]): Unit = {
println(reverse(List(1, 2, 3, 4)))
}
}ลำดับของการสะสม
โปรดสังเกตว่าการเติมด้านหน้าตัวสะสมจะกลับลำดับโดยธรรมชาติ สำหรับฟังก์ชันสร้างลิสต์ที่ต้องรักษาลำดับเดิม โดยทั่วไปคุณจะสร้างในลำดับย้อนกลับแล้วใช้ reverse ตอนท้าย หรือใช้โครงสร้างสำหรับการเติมท้ายที่มีประสิทธิภาพ
map แบบเรียกซ้ำแบบหาง
สร้างลิสต์ผลลัพธ์ด้วยตัวสะสม แล้วใช้ reverse เพียงครั้งเดียวตอนท้ายเพื่อคืนลำดับเดิม
import scala.annotation.tailrec
object Main {
def mapTail[A, B](xs: List[A])(f: A => B): List[B] = {
@tailrec
def loop(rem: List[A], acc: List[B]): List[B] = rem match {
case Nil => acc.reverse
case h :: t => loop(t, f(h) :: acc)
}
loop(xs, Nil)
}
def main(args: Array[String]): Unit = {
println(mapTail(List(1, 2, 3))(_ * 10))
}
}ความสัมพันธ์กับ foldLeft
รูปแบบตัวสะสมคือสิ่งที่ foldLeft ทำให้เป็นแนวคิดทั่วไป โดยส่งต่อตัวสะสมผ่านคอลเลกชันด้วยการเรียกซ้ำแบบหาง ฟังก์ชันที่ใช้ตัวสะสมและเขียนเองจำนวนมากสามารถเขียนใหม่เป็น foldLeft เพียงครั้งเดียวได้
@main def run(): Unit = {
val total = List(1, 2, 3, 4).foldLeft(0)(_ + _)
println(total)
}ควรใช้เมื่อใด
เลือกใช้รูปแบบตัวสะสมเมื่อฟังก์ชันแบบเรียกซ้ำประมวลผลโครงสร้างเชิงเส้นขนาดใหญ่ และอาจทำให้สแตกเต็มหากใช้รูปแบบเดิม วิธีนี้แลกกับรูปแบบที่เข้าใจได้ยากขึ้นเล็กน้อย เพื่อให้รับประกันความปลอดภัยต่อสแตก
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจของคุณเกี่ยวกับรูปแบบตัวสะสม
สรุปทบทวน
คุณได้เรียนรู้เรื่อง รูปแบบตัวสะสม:
- เก็บผลลัพธ์บางส่วนไว้ในพารามิเตอร์เพิ่มเติม
- คำนวณผลลัพธ์ ก่อนการเรียกซ้ำเพื่อให้เข้าสู่ตำแหน่งหาง
- ซ่อนตัวสะสมไว้เบื้องหลังฟังก์ชันสาธารณะที่สะอาด
- แนวคิดนี้ขยายไปเป็น
foldLeftได้
คำถามที่พบบ่อย
บทเรียน “รูปแบบตัวสะสม” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “รูปแบบตัวสะสม” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ 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 ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “รูปแบบตัวสะสม” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Scala for Backend Engineering & Functional Programming นี้ได้ไหม
ได้ บทเรียน Scala for Backend Engineering & Functional Programming ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- พื้นฐานการเรียกซ้ำ
- แอนโนเทชัน tailrec
- รูปแบบตัวสะสม
- แทรมโพลีน