2010-09-24 24 views
6

Tôi muốn có một cách thuận tiện để tạo ra một Iterable, cho một đối tượng ban đầu và một hàm để tạo ra đối tượng tiếp theo từ hiện tại, tiêu thụ bộ nhớ O (1) (tức là nó không lưu vào bộ nhớ cache kết quả cũ; nếu bạn muốn lặp lại lần thứ hai, hàm này sẽ được áp dụng lại).Tạo một O (1) -memory Lặp lại từ một đối tượng ban đầu và một hàm tạo đối tượng tiếp theo, trong Scala

Dường như không có thư viện hỗ trợ cho việc này. Trong Scala 2.8, phương pháp scala.collection.Iterable.iterate có chữ ký

def iterate [A] (start: A, len: Int)(f: (A) ⇒ A) : Iterable[A] 

vì thế nó đòi hỏi bạn phải xác định có bao nhiêu ứng dụng chức năng lặp bạn quan tâm trước thời hạn, và sự hiểu biết của tôi về các tài liệu là Iterable.iterate thực sự tính toán tất cả các giá trị ngay. Mặt khác, phương pháp scala.collection.Iterator.iterate có chữ ký

def iterate [T] (start: T)(f: (T) ⇒ T) : Iterator[T] 

trông tuyệt vời, nhưng chúng tôi chỉ nhận được một Iterator mà không cung cấp tất cả sự tiện lợi của map, filter và bạn bè.

Có phương pháp thư viện thuận tiện để sản xuất những gì tôi muốn không?

và nếu không,

Ai đó có thể đề nghị các Scala mã 'ngôn ngữ giao tiếp' để làm điều này?

Để tóm tắt, được đưa ra một đối tượng ban đầu a: A, và một hàm f: A => A, tôi muốn một TraversableLike (ví dụ, có thể là một Iterable) mà tạo ra a, f(a), f(f(a)), ..., và sử dụng O (1) bộ nhớ, với map, filter vv chức năng mà cũng trả lại cái gì đó là O (1) trong bộ nhớ.

+0

Một "đầu mối": đọc API một số chi tiết khác, tôi bắt đầu nghi ngờ rằng một câu trả lời hay sẽ đề cập đến 'TraversableViewLike', nhưng tôi cũng ngày càng bối rối. –

+2

Iterator * có * bản đồ, bộ lọc và bạn bè ... Bạn có chắc chắn họ sử dụng nhiều hơn bộ nhớ không đổi? – huynhjl

+0

Đó là sự thật, bản đồ và bộ lọc và như vậy có sẵn trên 'Iterator', và không thử bất cứ điều gì ngớ ngẩn như buộc' Iterator'. Nhưng một 'Iterable' sẽ thuận tiện hơn; tại sao tôi không nên mong đợi để có thể sử dụng 'đuôi' (trong đó, bất cứ khi nào' iterator' được gọi, nên loại bỏ các yếu tố đầu tiên thông qua một cuộc gọi đến 'next' trước khi giao lại' Iterator'), vv? (Trong thực tế, khi tôi cố gắng chuyển đổi mã của tôi từ mong đợi 'Iterable' để 'Iterator' s, đây là một cái gì đó tôi đã phải làm việc xung quanh.) –

Trả lời

3

Iterator.iterate bản demo với bộ lọc:

object I { 
    def main(args:Array[String]) { 
    val mb = 1024 * 1024 
    val gen = Iterator.iterate(new Array[Int](10 * mb)){arr => 
     val res = new Array[Int](10 * mb) 
     arr.copyToArray(res) 
     println("allocated 10mb") 
     res(0) = arr(0) + 1 // store iteration count in first elem of new array 
     res 
    } 
    // take 1 out of 100 
    val gen2 = gen filter (arr => arr(0) % 100 == 0) 
    // print first 10 filtered 
    gen2.take(10).foreach { arr => println("filtered " + arr(0)) } 
    } 
} 

(điều này có thể không làm việc trong REPL như là bước PRINT có thể gây chuyện với quản lý bộ nhớ)

JAVA_OPTS="-Xmx128m" scala -cp classes I sẽ cho thấy rằng các công trình lọc và là lười biếng. Nếu nó không được thực hiện trong bộ nhớ liên tục sẽ gây ra một lỗi heap (vì nó phân bổ một cái gì đó như 900 * 10mb).

Sử dụng JAVA_OPTS="-Xmx128m -verbose:gc" scala -cp classes I để xem các sự kiện thu thập rác.

+0

Cảm ơn các chi tiết đã thuyết phục tôi mọi thứ thực sự là O (1). Tôi sẽ thử cái này. –

10

Stream sẽ làm những gì bạn muốn, chỉ cần không giữ các ô; chỉ lặp qua các giá trị.

Đó là một quan niệm sai lầm phổ biến đáng buồn trôi nổi xung quanh luồng đó vốn đã lưu vào bộ nhớ cache mọi giá trị mà chúng tính toán.

Nếu bạn viết này:

val s1: Stream[Thing] = initialValue #:: «expression computing next value» 

sau đó thực sự mỗi giá trị được tạo ra bởi con suối được giữ lại, nhưng điều này là không cần thiết. Nếu bạn viết:

def s2: Stream[Thing] = initialValue #:: «expression computing next value» 

và nếu người gọi chỉ lặp trên giá trị của dòng nhưng không nhớ giá trị Suối bản thân (cụ thể là bất kỳ của các tế bào khuyết điểm của nó), sau đó không duy trì không mong muốn sẽ xảy ra. Tất nhiên, trong công thức này, mọi cuộc gọi tạo ra một Stream mới bắt đầu từ một giá trị ban đầu cố định. Điều đó không cần thiết:

def s3(start: Thing): Stream[Thing] = start #:: «expression computing next value» 

Điều bạn cần chú ý là chuyển một phương thức Stream. Làm như vậy sẽ nắm bắt phần đầu của luồng được truyền trong tham số phương thức. Một cách xung quanh điều này là xử lý luồng bằng mã đệ quy đuôi.

+0

Tôi không hiểu - Tôi cần để có thể vượt qua đối tượng này xung quanh với người tiêu dùng khác; có nghĩa là, không biết mã nào khác thực sự sẽ làm việc lặp lại. Tôi không thấy làm thế nào tôi có thể làm điều này mà không đi qua một tham chiếu đến người đứng đầu của 'Stream'. –

+0

Đó là một hạn chế. Như tôi đã nói, bạn sẽ phải cấu trúc mã để truyền 'Stream' thông qua các chuỗi được tối ưu hóa cho cuộc gọi đuôi. Nhưng mã "không xác định" này biết rằng nó nhận được một 'Stream' để nó biết nó không thể giữ lại các tham chiếu đến các ô đối lưu (stream-) của nó. –

+0

Không, điều này thực sự sẽ không làm. Tại sao mã "không rõ" biết gì? Nếu ai đó gọi vào mã của tôi, tại sao họ không chỉ xử lý giá trị trả về khi nói một 'Iterable'? –

2

Iterator chính là điều bạn muốn. Và iterator có bản đồ, bộ lọc, takeWhile và nhiều phương thức khác là O (1) trong bộ nhớ. Tôi không nghĩ rằng có một loại bộ sưu tập khác với O (1) trong bộ nhớ.

1
val it = new Iterable[Int] { 
    def iterator = Iterator.iterate(0)(_+1) 
    override 
    def toString: String = "Infinite iterable" 
} 

Đừng dùng thử REPL (ngoại trừ nhúng nó bên trong một đối tượng hoặc lớp), vì REPL sẽ cố gắng in và không sử dụng toString.

+0

Điều đó in "Infinite iterable" trong thân cây. – extempore

+0

@extempore Yay! –

+0

Ít nhất là theo tôi hiểu, 'nó map {_ + 1} mất 5' sẽ không chấm dứt, tuy nhiên, vì' map' sẽ cố gắng buộc 'Iterable'. –

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