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

foldLeft และ foldRight

ยุบคอลเลกชันให้เหลือค่าเดียว

foldLeft และ foldRight เป็นบทเรียน 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 บทเรียน

การพับคอลเลกชัน

การพับจะยุบคอลเลกชันให้เป็นค่าเดียว โดยรวมสมาชิกเข้ากับตัวสะสมซ้ำ ๆ

รูปแบบตัวสะสมที่คุณเรียนรู้มาคือสิ่งที่ fold สร้างเป็นนามธรรม แทนที่จะเขียนฟังก์ชันช่วยแบบเรียกซ้ำเอง คุณเพียงส่งค่าเริ่มต้นและฟังก์ชันรวมค่าเข้าไป

พื้นฐานของ foldLeft

foldLeft รับตัวสะสมเริ่มต้นและฟังก์ชัน (acc, element) แล้ววนผ่านคอลเลกชันจากซ้ายไปขวา

ในแต่ละขั้นตอน จะนำผลลัพธ์ของฟังก์ชันมาแทนที่ตัวสะสม

val xs = List(1, 2, 3, 4)
val total = xs.foldLeft(0)((acc, x) => acc + x)

@main def run(): Unit =
  println(total)  // 10

การจัดกลุ่มของ foldLeft

foldLeft จัดกลุ่มจากด้านซ้าย สำหรับ List(1, 2, 3) ที่มีค่าเริ่มต้นเป็น z จะคำนวณเป็น f(f(f(z, 1), 2), 3)

ตัวสะสมเป็นอาร์กิวเมนต์ด้านซ้าย จึงสะสมค่าไปขณะเคลื่อนไปทางขวาผ่านลิสต์

// List(1, 2, 3).foldLeft(0)(_ + _)
// = ((0 + 1) + 2) + 3
// = 6

พื้นฐานของ foldRight

foldRight ก็รวมสมาชิกเช่นกัน แต่เริ่มจากด้านขวา

ฟังก์ชันของ foldRight รับ (element, acc) โดยให้สมาชิกอยู่ด้านซ้ายและตัวสะสมอยู่ด้านขวา

val xs = List(1, 2, 3, 4)
val total = xs.foldRight(0)((x, acc) => x + acc)

@main def run(): Unit =
  println(total)  // 10

การจัดกลุ่มของ foldRight

foldRight จัดกลุ่มจากด้านขวา สำหรับ List(1, 2, 3) ที่มีค่าเริ่มต้นเป็น z จะคำนวณเป็น f(1, f(2, f(3, z)))

ค่าเริ่มต้นอยู่ทางขวาสุด และลิสต์จะถูกรวมเข้าด้านในจากส่วนท้าย

// List(1, 2, 3).foldRight(0)(_ + _)
// = 1 + (2 + (3 + 0))
// = 6

เมื่อทิศทางมีความสำคัญ

สำหรับการดำเนินการที่เปลี่ยนหมู่และสลับที่ได้ เช่น sum หรือผลคูณ การพับทั้งสองแบบจะให้คำตอบเดียวกัน

สำหรับการดำเนินการที่สลับที่ไม่ได้ เช่น การลบหรือการสร้างลิสต์ ทิศทางจะทำให้ผลลัพธ์เปลี่ยนไป จึงควรเลือกอย่างตั้งใจ

val xs = List(1, 2, 3)
val l = xs.foldLeft(0)(_ - _)   // ((0-1)-2)-3 = -6
val r = xs.foldRight(0)(_ - _)  // 1-(2-(3-0)) = 2

@main def run(): Unit =
  println((l, r))  // (-6, 2)

การสร้างลิสต์

foldRight เป็นตัวเลือกที่เหมาะตามธรรมชาติสำหรับสร้างลิสต์กลับมาตามลำดับเดิม เพราะทำงานจากส่วนท้ายเข้าด้านใน และการเติมด้านหน้าจะรักษาตำแหน่งของสมาชิก

วิธีนี้จะแปลงสมาชิกแต่ละตัวโดยยังคงลำดับไว้

val xs = List(1, 2, 3)
val doubled = xs.foldRight(List.empty[Int]) { (x, acc) =>
  (x * 2) :: acc
}

@main def run(): Unit =
  println(doubled)  // List(2, 4, 6)

foldLeft กลับลำดับ

หากสร้างลิสต์ด้วย foldLeft และเติมสมาชิกด้านหน้า ผลลัพธ์จะกลับลำดับ เพราะสมาชิกถูกเพิ่มจากด้านหน้าเมื่อเคลื่อนไปทางขวา

บางครั้งนี่ก็เป็นผลลัพธ์ที่คุณต้องการพอดี

val xs = List(1, 2, 3)
val rev = xs.foldLeft(List.empty[Int]) { (acc, x) =>
  x :: acc
}

@main def run(): Unit =
  println(rev)  // List(3, 2, 1)

ความปลอดภัยของสแตก

foldLeft เป็นการเรียกซ้ำแบบหางและทำงานเป็นลูป จึงปลอดภัยเมื่อใช้กับคอลเลกชันขนาดใหญ่มาก

foldRight กับลิสต์ไม่ใช่การเรียกซ้ำแบบหาง และอาจทำให้สแตกเต็มเมื่อลิสต์ยาวมาก หากไม่จำเป็นต้องเรียงจากขวาไปซ้าย ควรเลือกใช้ foldLeft

// Safe even for millions of elements:
val n = (1 to 1000000).foldLeft(0L)(_ + _)

// foldRight on a long List risks StackOverflowError

การเปลี่ยนชนิดของผลลัพธ์

ชนิดของตัวสะสมอาจแตกต่างจากชนิดของสมาชิกได้

ในที่นี้ เราพับลิสต์จำนวนเต็มให้เป็นสตริง ดังนั้นค่าเริ่มต้นจึงเป็นสตริงว่าง และแต่ละขั้นตอนจะนำค่ามาต่อท้าย

ชนิดของ fold จะถูกกำหนดโดยค่าเริ่มต้น

val xs = List(1, 2, 3)
val s = xs.foldLeft("")((acc, x) => acc + x.toString)

@main def run(): Unit =
  println(s)  // "123"

Fold ในฐานะเครื่องมือสารพัดประโยชน์

การดำเนินการกับลิสต์จำนวนมากเป็นกรณีเฉพาะของ fold ได้แก่ sum ผลคูณ ความยาว max map filter และ reverse

การมองเห็น fold ที่อยู่เบื้องหลังการดำเนินการเหล่านี้ช่วยให้คุณเขียนโค้ดแบบประกาศที่กระชับ แทนการเขียนการเรียกซ้ำขึ้นเอง

val xs = List(4, 1, 7, 3)
val maxV = xs.foldLeft(Int.MinValue)(_ max _)
val len  = xs.foldLeft(0)((acc, _) => acc + 1)

@main def run(): Unit =
  println((maxV, len))  // (7, 4)

ตรวจสอบความเข้าใจ

วิเคราะห์ทิศทางของ fold และตำแหน่งของค่าเริ่มต้น

สรุปทบทวน

foldLeft เดินจากซ้ายไปขวา โดยมีตัวสะสมอยู่ด้านซ้าย และคำนวณเป็น ((z op a) op b) op c เป็นการเรียกซ้ำแบบหางและปลอดภัยต่อสแตก

foldRight เดินจากขวาไปซ้าย โดยมีค่าเริ่มต้นอยู่ด้านขวา และคำนวณเป็น a op (b op (c op z)) เหมาะสำหรับการสร้างลิสต์โดยรักษาลำดับ แต่สามารถทำให้สแตกเต็มเมื่อลิสต์ยาวมาก

ค่าเริ่มต้นจะกำหนดชนิดของผลลัพธ์ ดังนั้น fold จึงสามารถแปลงคอลเลกชันให้เป็นค่าใด ๆ ได้

คำถามที่พบบ่อย

บทเรียน “foldLeft และ foldRight” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “foldLeft และ foldRight” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Scala for Backend Engineering & Functional Programming ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “foldLeft และ foldRight”

ยุบคอลเลกชันให้เหลือค่าเดียว คุณปฏิบัติ Scala for Backend Engineering & Functional Programming ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Scala for Backend Engineering & Functional Programming หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Scala for Backend Engineering & Functional Programming บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “foldLeft และ foldRight” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Scala for Backend Engineering & Functional Programming นี้ได้ไหม

ได้ บทเรียน Scala for Backend Engineering & Functional Programming ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

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

  1. คิดแบบเรียกซ้ำ
  2. รูปแบบตัวสะสม
  3. foldLeft และ foldRight
  4. reduce และ aggregate
← กลับไปที่ Scala for Backend Engineering & Functional Programming