0Pricing
Scala for Backend Engineering & Functional Programming · บทเรียน

รูปแบบตัวสะสม

เปลี่ยนเป็นการเรียกซ้ำแบบหาง

รูปแบบตัวสะสม เป็นบทเรียน 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 ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. พื้นฐานการเรียกซ้ำ
  2. แอนโนเทชัน tailrec
  3. รูปแบบตัวสะสม
  4. แทรมโพลีน
← กลับไปที่ Scala for Backend Engineering & Functional Programming