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

การคลี่รูป for

map และ flatMap

การคลี่รูป for เป็นบทเรียน Scala for Backend Engineering & Functional Programming ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Scala for Backend Engineering & Functional Programming และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Scala for Backend Engineering & Functional Programming มีบทเรียนทั้งหมด 4 บทเรียน

for-comprehension คืออะไร

for-comprehension ใน Scala คือไวยากรณ์พิเศษสำหรับทำงานกับคอลเลกชันและชนิดข้อมูลที่เป็นภาชนะอื่น ๆ รูปแบบดูคล้ายลูปแบบคำสั่ง แต่จริง ๆ แล้วเป็น รูปแบบย่อทางไวยากรณ์ ที่คอมไพเลอร์จะแปลงกลับเป็นการเรียก map, flatMap, filter และ foreach

การเข้าใจการแปลงกลับนี้ช่วยให้คุณวิเคราะห์ for-comprehension ได้ทุกแบบ แม้จะใช้กับชนิดข้อมูลที่กำหนดเอง

object Main {
  def main(args: Array[String]): Unit = {
    val result = for (x <- List(1, 2, 3)) yield x * 10
    println(result)
  }
}

ตัวสร้างเดียวจะกลายเป็น map

เมื่อ for-comprehension มีตัวสร้างเพียงหนึ่งตัวและใช้ yield คอมไพเลอร์จะแปลงเป็นการเรียก map เพียงครั้งเดียว

  • for (x <- xs) yield f(x)
  • จะกลายเป็น xs.map(x => f(x))
object Main {
  def main(args: Array[String]): Unit = {
    val a = for (x <- List(1, 2, 3)) yield x + 1
    val b = List(1, 2, 3).map(x => x + 1)
    println(a == b)
  }
}

คีย์เวิร์ด yield

คีย์เวิร์ด yield บอกให้ Scala รวบรวมผลลัพธ์เป็นคอลเลกชันใหม่ชนิดเดียวกัน

หากไม่มี yield for-comprehension จะทำผลข้างเคียงกับแต่ละสมาชิกและคืนค่าเป็น Unit หรือถูกแปลงเป็น foreach

object Main {
  def main(args: Array[String]): Unit = {
    // No yield: side effect only
    for (x <- List("a", "b")) println(x)
    // With yield: builds a new List
    val upper = for (x <- List("a", "b")) yield x.toUpperCase
    println(upper)
  }
}

ตัวสร้างสองตัวจะกลายเป็น flatMap + map

เมื่อมีตัวสร้างสองตัวขึ้นไป ตัวสร้างด้านนอกจะกลายเป็น flatMap และตัวสร้างชั้นในสุดจะกลายเป็น map

  • for (x <- xs; y <- ys) yield f(x, y)
  • จะกลายเป็น xs.flatMap(x => ys.map(y => f(x, y)))
object Main {
  def main(args: Array[String]): Unit = {
    val pairs = for {
      x <- List(1, 2)
      y <- List("a", "b")
    } yield (x, y)
    println(pairs)
  }
}

ดู flatMap ที่เทียบเท่ากัน

ในที่นี้ เราเขียนตรรกะเดียวกันทั้งสองรูปแบบ การพิสูจน์ว่าโค้ดทั้งสองเทียบเท่ากันแสดงให้เห็นว่า for-comprehension ไม่ได้เพิ่มความมหัศจรรย์ใด ๆ แต่ช่วยให้อ่านง่ายขึ้นเท่านั้น

object Main {
  def main(args: Array[String]): Unit = {
    val sugar = for {
      x <- List(1, 2)
      y <- List(10, 20)
    } yield x + y

    val desugared = List(1, 2).flatMap(x => List(10, 20).map(y => x + y))

    println(sugar)
    println(sugar == desugared)
  }
}

ทำไมตัวสร้างด้านนอกจึงใช้ flatMap

สมาชิกแต่ละตัวจากคอลเลกชันด้านนอกจะสร้างคอลเลกชันทั้งหมดของผลลัพธ์ด้านใน หากเราใช้ map กับตัวสร้างด้านนอก เราจะได้คอลเลกชันที่ซ้อนกัน เช่น List(List(...), List(...))

flatMap จะทำให้ลิสต์ที่ซ้อนกันเหล่านั้นแบนลงเป็นผลลัพธ์เดียวที่ไม่ซ้อนกัน

object Main {
  def main(args: Array[String]): Unit = {
    val nested = List(1, 2).map(x => List(10, 20).map(y => x + y))
    val flat   = List(1, 2).flatMap(x => List(10, 20).map(y => x + y))
    println(nested)
    println(flat)
  }
}

for ที่ไม่มี yield คือ foreach

ลูป for ที่ไม่มี yield จะถูกแปลงเป็น foreach โดยทำงานกับส่วนคำสั่งเพื่อให้เกิดผลข้างเคียง และคืนค่าเป็น Unit

  • for (x <- xs) doSomething(x)
  • จะกลายเป็น xs.foreach(x => doSomething(x))
object Main {
  def main(args: Array[String]): Unit = {
    val r: Unit = for (x <- List(1, 2, 3)) print(x + " ")
    println()
    println("Return type is Unit")
  }
}

ชนิดผลลัพธ์ตามตัวสร้างแรก

ชนิดของผลลัพธ์จะกำหนดโดยคอลเลกชันของตัวสร้างแรก for ที่ทำงานกับ List จะให้ผลเป็น List ทำงานกับ Set จะให้ผลเป็น Set และทำงานกับ Option จะให้ผลเป็น Option

object Main {
  def main(args: Array[String]): Unit = {
    val fromList = for (x <- List(1, 2, 2, 3)) yield x
    val fromSet  = for (x <- Set(1, 2, 2, 3)) yield x
    println(fromList)
    println(fromSet)
  }
}

การผูกค่าด้วย =

ภายใน for-comprehension คุณสามารถผูกค่าระหว่างทางด้วย = วิธีนี้ช่วยหลีกเลี่ยงการคำนวณซ้ำและทำให้อ่านง่ายขึ้น

รูปแบบนี้จะถูกแปลงเป็น map ที่พาค่าเพิ่มเติมไปด้วยในทูเพิล

object Main {
  def main(args: Array[String]): Unit = {
    val result = for {
      x <- List(1, 2, 3)
      doubled = x * 2
    } yield doubled + 1
    println(result)
  }
}

ใช้ได้กับทุกชนิดที่มี map/flatMap

เนื่องจากการแปลงกลับนี้อาศัยเพียง map และ flatMap for-comprehension จึงใช้ได้กับชนิดข้อมูลใดก็ตามที่กำหนดเมธอดเหล่านี้ เช่น Option, Either, Try, Future และคลาสที่คุณสร้างเอง

object Main {
  def main(args: Array[String]): Unit = {
    val combined = for {
      a <- Some(2)
      b <- Some(3)
    } yield a * b
    println(combined)
  }
}

ประโยชน์สำคัญคืออ่านง่าย

ลองเปรียบเทียบลำดับการเรียก flatMap/map ที่ซ้อนกันลึก ๆ กับ for-comprehension ที่กระชับ ทั้งสองแบบคอมไพล์เป็นสิ่งเดียวกัน แต่แบบ for อ่านจากบนลงล่างได้เหมือนสูตรการทำงาน

object Main {
  def main(args: Array[String]): Unit = {
    val chained = List(1, 2).flatMap(a => List(3, 4).map(b => a * b))
    val readable = for {
      a <- List(1, 2)
      b <- List(3, 4)
    } yield a * b
    println(chained == readable)
  }
}

ตรวจสอบความเข้าใจอย่างรวดเร็ว

คอมไพเลอร์จะแปลง for-comprehension ที่มีตัวสร้างสองตัวและมี yield อย่างไร

สรุปทบทวน

คุณได้เรียนรู้วิธีที่ for-comprehension ถูกแปลงกลับ:

  • ตัวสร้างหนึ่งตัว + yield → map
  • ตัวสร้างหลายตัว + yield → flatMap สำหรับด้านนอก + map สำหรับด้านใน
  • ไม่มี yield → foreach ที่คืนค่าเป็น Unit
  • การผูกค่า val = จะเพิ่มค่าระหว่างทาง
  • ชนิดผลลัพธ์จะตามตัวสร้างแรก

ชนิดข้อมูลใดก็ตามที่มี map และ flatMap สามารถใช้ใน for-comprehension ได้

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

บทเรียน “การคลี่รูป for” ฟรีหรือไม่

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

คุณจะเรียนรู้อะไรในบทเรียน “การคลี่รูป for”

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

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

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

บทเรียน “การคลี่รูป for” ใช้เวลานานแค่ไหน

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

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

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

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

  1. การคลี่รูป for
  2. การกรองด้วย if
  3. ตัวสร้างหลายตัว
  4. การใช้ร่วมกับ Option และ Future
← กลับไปที่ Scala for Backend Engineering & Functional Programming