Scala: Sự khác biệt giữa các đặc điểm Có thể duyệt và lặp lại trong các bộ sưu tập Scala là gì?


98

Tôi đã xem xét câu hỏi này nhưng vẫn không hiểu sự khác biệt giữa các đặc điểm Có thể duyệt và Có thể duyệt. Ai đó có thể giải thích?


2
Không có Traversabletrong Scala 2.13 (nó vẫn được giữ như một bí danh NỮA cho Iterableđến 2.14)
Xavier Guihot

Câu trả lời:


121

Nói một cách đơn giản, các trình vòng lặp giữ trạng thái, các trình duyệt thì không.

Một Traversablecó một phương pháp trừu tượng: foreach. Khi bạn gọi foreach, bộ sưu tập sẽ cung cấp cho hàm đã truyền tất cả các phần tử mà nó giữ, cái này đến phần tử khác.

Mặt khác, một Iterablephương thức có as trừu tượng iterator, trả về một Iterator. Bạn có thể gọi nextmột Iteratorđể nhận phần tử tiếp theo tại thời điểm bạn chọn. Cho đến khi bạn làm vậy, nó phải theo dõi vị trí của nó trong bộ sưu tập và những gì tiếp theo.


4
Nhưng Iterablemở rộng Traversable, vì vậy tôi đoán bạn có nghĩa là Traversables mà không phải Iterablelà s.
Robin Green

4
@RobinGreen Ý tôi là tuân thủ Traversablegiao diện không yêu cầu giữ trạng thái, trong khi tuân thủ Iteratorgiao diện thì có.
Daniel C. Sobral

10
Traversables Iterablekhông giữ bất kỳ trạng thái lặp lại nào. Nó Iteratorđược tạo và trả lại bởi Iterablecái giữ trạng thái.
Graham Lea,

1
Cần lưu ý rằng kể từ 2.13 đặc điểm Traversable không được dùng nữa. Để trích dẫn Stefan Zeiger, "Tính trừu tượng của Traversable không có sức nặng trong thư viện hiện tại và có khả năng sẽ không xuất hiện trở lại trong thiết kế mới. Mọi thứ chúng tôi muốn làm đều có thể được thể hiện bằng Iterable."
Igor Urisman

226

Hãy coi đó là sự khác biệt giữa thổi và hút.

Khi bạn gọi một Traversables foreachhoặc các phương thức dẫn xuất của nó, nó sẽ thổi các giá trị của nó vào hàm của bạn tại một thời điểm - vì vậy nó có quyền kiểm soát việc lặp lại.

Với giá trị Iteratortrả về bởi một Iterablemặc dù, bạn hút các giá trị ra khỏi nó, tự mình kiểm soát thời điểm chuyển sang giá trị tiếp theo.


49
Mọi người thường gọi đây là đẩykéo thay vì thổihút , nhưng tôi thích sự cởi mở của bạn.
Martijn

2
Không bao giờ quên điều này khi được hỏi trong cuộc phỏng vấn tiếp theo của tôi
thestephenstanton 14/08/18

23

tl; dr Iterables là những Traversablesthứ có thể tạo ra trạng tháiIterators


Đầu tiên, hãy biết rằng đó Iterablelà trang con của Traversable.

Thứ hai,

  • Traversableyêu cầu triển khai foreachphương thức, được sử dụng bởi mọi thứ khác.

  • Iterableyêu cầu triển khai iteratorphương thức, được sử dụng bởi mọi thứ khác.

Ví dụ, việc findáp Traversabledụng cho các sử dụng foreach(thông qua một để hiểu) và ném một BreakControlngoại lệ để dừng lặp lại khi đã tìm thấy phần tử thỏa mãn.

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
  }
}

Ngược lại, hàm Iterabletrừ sẽ ghi đè triển khai này và gọi findhàm Iterator, chỉ đơn giản là dừng lặp lại khi phần tử được tìm thấy:

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
  }
}

Sẽ rất tốt nếu bạn không ném ra các ngoại lệ để Traversablelặp lại, nhưng đó là cách duy nhất để lặp lại một phần khi chỉ sử dụng foreach.

Từ một góc độ, Iterablelà đặc điểm đòi hỏi cao hơn / mạnh mẽ hơn, vì bạn có thể dễ dàng triển khai foreachbằng cách sử dụng iterator, nhưng bạn không thể thực sự triển khai iteratorbằng cách sử dụng foreach.


Tóm lại, Iterablecung cấp một cách để tạm dừng, tiếp tục hoặc ngừng lặp lại thông qua trạng thái Iterator. Với Traversable, tất cả hoặc không có gì (không có ngoại lệ cho điều khiển luồng).

Hầu hết thời gian điều đó không quan trọng và bạn sẽ muốn có giao diện chung hơn. Nhưng nếu bạn cần kiểm soát tùy chỉnh nhiều hơn đối với việc lặp lại, bạn sẽ cần một Iterator, mà bạn có thể truy xuất từ ​​một Iterable.


1

Câu trả lời của Daniel nghe hay đấy. Hãy để tôi xem nếu tôi có thể diễn đạt nó theo cách của riêng tôi.

Vì vậy, một Iterable có thể cung cấp cho bạn một trình vòng lặp, cho phép bạn duyệt qua các phần tử một cách lần lượt (sử dụng next ()) và dừng lại và đi tùy ý. Để làm điều đó, trình lặp cần giữ một "con trỏ" nội bộ đến vị trí của phần tử. Nhưng một Traversable cung cấp cho bạn phương pháp, foreach, để duyệt qua tất cả các phần tử cùng một lúc mà không dừng lại.

Một cái gì đó như Phạm vi (1, 10) chỉ cần có 2 số nguyên ở trạng thái có thể chuyển ngang. Nhưng Phạm vi (1, 10) như một Lặp lại cung cấp cho bạn một trình vòng lặp cần sử dụng 3 số nguyên cho trạng thái, một trong số đó là chỉ mục.

Xem xét rằng Traversable cũng cung cấp foldLeft, foldRight, bước trước của nó cần phải duyệt các phần tử theo một thứ tự đã biết và cố định. Do đó, có thể triển khai một trình vòng lặp cho một Trình duyệt. Ví dụ: def iterator = toList.iterator

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.