2011-12-20 35 views
32

Có các câu hỏi khác như Scala: What is the difference between Traversable and Iterable traits in Scala collections?How would I get the sum of squares of two Lists in Scala? trả lời câu hỏi một phần. Tôi cảm thấy một câu hỏi bao gồm tất cả điều này ở một nơi hợp lý.Ngữ nghĩa của Scala Traversable, Iterable, Sequence, Stream và View?

+3

Tôi đang bỏ phiếu để đóng 1) vì đây không thực sự là một câu hỏi cụ thể nhưng đến nay nói chung, 2) vì thông tin có khả năng phát triển cũ khi Scala phát triển và 3) vì trường hợp chung đã được xác định rõ [Tài liệu của Scala] (http://www.scala-lang.org/docu/files/collections-api/collections.html), đây có lẽ là một nguồn tài nguyên tốt hơn cho các cuộc điều tra rộng lớn như vậy (và Scala, không giống như nhiều ngôn ngữ khác, có tài liệu khá rộng, nếu đôi khi khá khó khăn). – ig0774

+2

@ ig0774 2) Theo tôi, đó là khái niệm quá cốt lõi của bộ sưu tập scala sẽ được thay đổi đáng kể trong tương lai có thể quan sát được. –

+7

@ ig0774 Trái ngược với ba điểm của bạn, tôi upvoted thay vì bỏ phiếu để đóng vì 1) Tôi cảm thấy câu hỏi này thường có giá trị, và đủ hữu hình để được trả lời ngắn gọn, 2) bởi vì nó nằm gần trái tim của sự hiểu biết các bộ sưu tập Scala hiện tại thư viện, và 3) tài liệu hiện tại, mặc dù mở rộng, rất phổ biến; thật khó để có được bức tranh lớn. –

Trả lời

33

Traversable là đầu phân cấp bộ sưu tập. Phương pháp chính của nó là 'foreach' vì vậy nó cho phép làm điều gì đó cho từng phần tử của bộ sưu tập.

An Iterable có thể tạo Iterator, dựa trên đó foreach có thể được triển khai. Điều này định nghĩa một số thứ tự của các phần tử, mặc dù thứ tự đó có thể thay đổi cho mọi Iterator.

Seq (uence) là một Iterable nơi thứ tự của các phần tử được cố định. Do đó, có ý nghĩa khi nói về chỉ mục của một phần tử.

Luồng là các chuỗi chậm. I E. các yếu tố của một luồng có thể không được tính toán trước khi chúng được truy cập. Điều này làm cho nó có thể làm việc với các chuỗi vô hạn như trình tự của tất cả các số nguyên.

Lượt xem là các phiên bản không nghiêm ngặt của bộ sưu tập. Các phương thức như bộ lọc và bản đồ trên chế độ xem chỉ thực thi các hàm được truyền khi phần tử tương ứng được truy cập. Vì vậy, một bản đồ trên một bộ sưu tập lớn trở lại ngay lập tức bởi vì nó chỉ tạo ra một wrapper xung quanh bộ sưu tập ban đầu. Chỉ khi một người truy cập vào một phần tử, ánh xạ sẽ được thực thi (đối với phần tử đó). Lưu ý rằng View không phải là một lớp, nhưng có rất nhiều lớp XxxView cho các bộ sưu tập khác nhau.

+9

Chế độ xem không lười biếng, không nghiêm ngặt. Laziness yêu cầu bộ nhớ đệm, luồng nào làm, nhưng các khung nhìn thì không. –

+0

Cảm ơn bạn đã làm rõ. cố định nó trong câu trả lời. –

+4

@ DanielC.Sobral "Laziness yêu cầu bộ nhớ đệm" - um, cái gì? –

2

Một nhận xét tôi muốn thêm về luồng so với trình lặp. Cả hai luồng và trình lặp có thể được sử dụng để triển khai các bộ sưu tập dài, không nghiêm ngặt, có khả năng vô hạn mà không tính giá trị cho đến khi cần. Tuy nhiên, có một vấn đề khó khăn với "thực thi sớm" phát sinh khi thực hiện điều này, có thể tránh được bằng cách sử dụng trình lặp nhưng không phải luồng, và trong quá trình chỉ ra một sự khác biệt quan trọng giữa hai ngữ nghĩa. Điều này có lẽ được minh họa rõ ràng nhất như sau:

def runiter(start: Int) { 
    // Create a stream that returns successive integers on demand, e.g. 3, 4, 5, .... 
    val iter = { 
    def loop(v: Int): Stream[Int] = { println("I computed a value", v); v} #:: loop(v+1) 
    loop(start) 
    } 
    // Now, sometime later, we retrieve the values .... 
    println("about to loop") 
    for (x <- iter) { 
    if (x < 10) println("saw value", x) else return 
    } 
} 

Mã này tạo luồng vô hạn bắt đầu từ một giá trị nhất định và trả về các số nguyên liên tiếp. Nó được sử dụng như là một stand-in cho mã phức tạp hơn có thể, ví dụ, mở một kết nối internet và trả về các giá trị từ kết nối khi cần thiết.

Kết quả:

scala> runiter(3) 
(I computed a value,3) 
about to loop 
(saw value,3) 
(I computed a value,4) 
(saw value,4) 
(I computed a value,5) 
(saw value,5) 
(I computed a value,6) 
(saw value,6) 
(I computed a value,7) 
(saw value,7) 
(I computed a value,8) 
(saw value,8) 
(I computed a value,9) 
(saw value,9) 
(I computed a value,10) 

Lưu ý cẩn thận như thế nào thực hiện được yêu cầu để tính toán giá trị đầu tiên xảy ra TRƯỚC nơi giá trị của dòng đang thực sự sử dụng. Nếu việc thực hiện ban đầu này bao gồm, ví dụ, mở một tệp hoặc kết nối internet và có một sự chậm trễ lâu sau khi tạo luồng và trước khi bất kỳ giá trị nào được sử dụng, điều này có thể rất có vấn đề - bạn sẽ kết thúc với một bộ mô tả tệp đang mở xung quanh, và tồi tệ hơn, kết nối internet của bạn có thể hết thời gian, khiến toàn bộ mọi thứ thất bại.

Một nỗ lực đơn giản để sửa chữa nó bằng một dòng trống ban đầu không hoạt động:

def runiter(start: Int) { 
    // Create a stream that returns successive integers on demand, e.g. 3, 4, 5, .... 
    val iter = { 
    def loop(v: Int): Stream[Int] = { println("I computed a value", v); v} #:: loop(v+1) 
    Stream[Int]() ++ loop(start) 
    } 
    // Now, sometime later, we retrieve the values .... 
    println("about to loop") 
    for (x <- iter) { 
    if (x < 10) println("saw value", x) else return 
    } 
} 

quả (giống như trước đó):

scala> runiter(3) 
(I computed a value,3) 
about to loop 
(saw value,3) 
(I computed a value,4) 
(saw value,4) 
(I computed a value,5) 
(saw value,5) 
(I computed a value,6) 
(saw value,6) 
(I computed a value,7) 
(saw value,7) 
(I computed a value,8) 
(saw value,8) 
(I computed a value,9) 
(saw value,9) 
(I computed a value,10) 

Tuy nhiên, bạn có thể sửa lỗi này bằng thay đổi luồng thành một trình lặp với một trình vòng lặp trống ban đầu, mặc dù nó không rõ ràng rằng đây là trường hợp:

def runiter(start: Int) { 
    // Create an iterator that returns successive integers on demand, e.g. 3, 4, 5, .... 
    val iter = { 
    def loop(v: Int): Iterator[Int] = { println("I computed a value", v); Iterator(v)} ++ loop(v+1) 
    Iterator[Int]() ++ loop(start) 
    } 
    // Now, sometime later, we retrieve the values .... 
    println("about to loop") 
    for (x <- iter) { 
    if (x < 10) println("saw value", x) else return 
    } 
} 

Kết quả:

scala> runiter(3) 
about to loop 
(I computed a value,3) 
(saw value,3) 
(I computed a value,4) 
(saw value,4) 
(I computed a value,5) 
(saw value,5) 
(I computed a value,6) 
(saw value,6) 
(I computed a value,7) 
(saw value,7) 
(I computed a value,8) 
(saw value,8) 
(I computed a value,9) 
(saw value,9) 
(I computed a value,10) 

Lưu ý rằng nếu bạn không thêm các iterator rỗng ban đầu, bạn sẽ chạy vào các vấn đề thực hiện sớm tương tự như với những con suối.

+3

bạn có thể thay đổi "val iter" thành "def iter", trong phiên bản luồng. Điều này sẽ làm các trick. –

Các vấn đề liên quan