Scala: อะไรคือความแตกต่างระหว่างลักษณะ Traversable และ Iterable ในคอลเล็กชัน Scala


98

ฉันได้ดูคำถามนี้แล้วแต่ยังไม่เข้าใจความแตกต่างระหว่างลักษณะที่สามารถทำซ้ำได้และลักษณะที่ผ่านได้ ใครช่วยอธิบายหน่อย


2
ไม่มีอีกแล้วTraversableในScala 2.13 (ยังคงถูกเก็บไว้เป็นนามแฝงที่เลิกใช้งานIterableจนถึง 2.14)
Xavier Guihot

คำตอบ:


121

พูดง่ายๆก็คือตัววนซ้ำจะรักษาสถานะการข้ามผ่านไม่ได้

A Traversableมีวิธีนามธรรมอย่างหนึ่ง: foreach. เมื่อคุณโทรforeach, คอลเลกชันจะฟีดฟังก์ชั่นผ่านองค์ประกอบทั้งหมดที่จะช่วยให้คนหนึ่งหลังจากที่อื่น ๆ

ในทางกลับกันวิธีIterablehas as abstract iteratorซึ่งส่งกลับค่าIterator. คุณสามารถโทรnextหาIteratorเพื่อรับองค์ประกอบถัดไปในเวลาที่คุณเลือก คุณต้องติดตามว่ามันอยู่ที่ใดในคอลเลกชันและมีอะไรต่อไป


4
แต่IterableขยายออกไปTraversableดังนั้นฉันเดาว่าคุณหมายถึงTraversables นั่นไม่ใช่Iterables
Robin Green

4
@RobinGreen ฉันหมายถึงการปฏิบัติตามTraversableอินเทอร์เฟซไม่จำเป็นต้องมีการรักษาสถานะในขณะที่ปฏิบัติตามIteratorอินเทอร์เฟซ
Daniel C. Sobral

10
Traversables ที่Iterableไม่คงสถานะการวนซ้ำใด ๆ เป็นสิ่งที่Iteratorสร้างและส่งคืนโดยสิ่งIterableที่รักษาสถานะ
Graham Lea

2
เป็นสิ่งที่ควรทราบว่า ณ วันที่ 2.13 traversable เลิกใช้งานแล้ว เพื่ออ้างถึง Stefan Zeiger "สิ่งที่เป็นนามธรรม Traversable ไม่ได้รับน้ำหนักในห้องสมุดปัจจุบันและมีแนวโน้มว่าจะไม่ปรากฏในรูปแบบใหม่ทุกสิ่งที่เราต้องการทำสามารถแสดงได้ด้วย Iterable"
Igor Urisman

227

คิดว่ามันเป็นความแตกต่างระหว่างการเป่าและการดูด

เมื่อคุณโทรหาTraversablesforeachหรือเมธอดที่ได้รับมามันจะระเบิดค่าในฟังก์ชันของคุณทีละฟังก์ชันดังนั้นจึงมีการควบคุมการวนซ้ำ

ด้วยการIteratorส่งคืนโดยIterableแม้ว่าคุณจะดูดค่าออกจากมันและควบคุมว่าเมื่อใดที่จะย้ายไปยังค่าถัดไปด้วยตัวคุณเอง


50
ผู้คนใช้เรียกสิ่งนี้ว่าการผลักและดึงแทนที่จะเป่าและดูดแต่ฉันชอบการเปิดใจกว้างของคุณ
Martijn

2
อย่าลืมสิ่งนี้เมื่อถูกถามในการสัมภาษณ์ครั้งต่อไปของฉัน
thestephenstanton

24

TL; DR Iterablesอยู่Traversablesที่สามารถผลิต statefulIterators


ขั้นแรกให้รู้ว่าIterableเป็นลบของTraversableของ

ประการที่สอง

  • Traversableต้องใช้foreachวิธีการซึ่งใช้โดยทุกสิ่งทุกอย่าง

  • Iterableต้องใช้iteratorวิธีการซึ่งใช้โดยทุกสิ่งทุกอย่าง

ตัวอย่างเช่นการดำเนินการfindสำหรับการTraversableใช้งานforeach(ผ่าน a เพื่อความเข้าใจ) และแสดงBreakControlข้อยกเว้นเพื่อหยุดการทำซ้ำเมื่อพบองค์ประกอบที่น่าพอใจ

trait TravserableLike {
  def find(p: A => Boolean): Option[A] = {
    var result: Option[A] = None
    breakable {
      for (x <- this)
        if (p(x)) { result = Some(x); break }
    }
    result
  }
}

ในทางตรงกันข้ามการIterableลบจะลบล้างการใช้งานนี้และเรียกfindใช้Iteratorซึ่งจะหยุดการทำซ้ำเมื่อพบองค์ประกอบ:

trait Iterable {
  override /*TraversableLike*/ def find(p: A => Boolean): Option[A] =
    iterator.find(p)
}

trait Iterator {
  def find(p: A => Boolean): Option[A] = {
    var res: Option[A] = None
      while (res.isEmpty && hasNext) {
        val e = next()
        if (p(e)) res = Some(e)
      }
    res
  }
}

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

จากมุมมองหนึ่งIterableคือการเรียกร้องมากขึ้น / ลักษณะที่มีประสิทธิภาพในขณะที่คุณสามารถดำเนินการforeachโดยใช้iteratorแต่คุณไม่สามารถจริงๆดำเนินการโดยใช้iteratorforeach


โดยสรุปIterableมีวิธีที่จะหยุดชั่วคราวหรือหยุดย้ำผ่าน Iteratorstateful ด้วยTraversableมันทั้งหมดหรือไม่มีอะไร (ยกเว้นซองสำหรับการควบคุมการไหล)

โดยส่วนใหญ่ไม่สำคัญและคุณจะต้องการอินเทอร์เฟซที่กว้างขึ้น แต่ถ้าคุณต้องการการควบคุมการทำซ้ำแบบกำหนดเองมากขึ้นคุณจะต้องมีIteratorซึ่งคุณสามารถเรียกดูได้จากIterableไฟล์.


1

คำตอบของแดเนียลฟังดูดี ขอดูว่าจะใส่คำพูดของตัวเองได้ไหม

ดังนั้น Iterable สามารถให้คุณทำซ้ำซึ่งช่วยให้คุณสำรวจองค์ประกอบทีละรายการ (โดยใช้ next ()) และหยุดและไปตามที่คุณต้องการ ในการทำเช่นนั้นตัววนซ้ำจำเป็นต้องให้ "ตัวชี้" ภายในอยู่ในตำแหน่งขององค์ประกอบ แต่ Traversable ช่วยให้คุณมีวิธี foreach เพื่อสำรวจองค์ประกอบทั้งหมดพร้อมกันโดยไม่หยุด

บางอย่างเช่น Range (1, 10) จำเป็นต้องมีจำนวนเต็ม 2 จำนวนเท่านั้นที่เป็นสถานะ Traversable แต่ช่วง (1, 10) เป็นตัวทำซ้ำได้จะทำให้คุณมีตัวทำซ้ำซึ่งจำเป็นต้องใช้จำนวนเต็ม 3 ตัวสำหรับสถานะซึ่งหนึ่งในนั้นคือดัชนี

เมื่อพิจารณาว่า Traversable ยังมี foldLeft, foldRight ซึ่ง foreach จำเป็นต้องสำรวจองค์ประกอบตามลำดับที่ทราบและคงที่ ดังนั้นจึงเป็นไปได้ที่จะใช้ตัววนซ้ำสำหรับ Traversable เช่น def iterator = toList.iterator

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.