2012-02-17 19 views
10

Im tìm cách mở rộng trình vòng lặp để tạo phương thức mới takeWhileInclusive, hoạt động như takeWhile nhưng bao gồm phần tử cuối cùng.Scala, mở rộng trình lặp số

Vấn đề của tôi là cách tốt nhất để mở rộng trình vòng lặp để trả về trình lặp mới mà tôi muốn được đánh giá lười biếng. Đến từ nền C# tôi sử dụng bình thường IEnumerable và sử dụng từ khóa yield, nhưng một tùy chọn như vậy dường như không tồn tại trong Scala.

ví dụ tôi có thể có

List(0,1,2,3,4,5,6,7).iterator.map(complex time consuming algorithm).takeWhileInclusive(_ < 6) 

như vậy trong trường hợp này takeWhileInclusive sẽ chỉ phải giải quyết các vị trên các giá trị cho đến khi tôi nhận được một kết quả lớn hơn 6, và nó sẽ bao gồm kết quả đầu tiên này

cho đến nay tôi có:

object ImplicitIterator { 
    implicit def extendIterator(i : Iterator[Any]) = new IteratorExtension(i) 
} 

class IteratorExtension[T <: Any](i : Iterator[T]) { 
    def takeWhileInclusive(predicate:(T) => Boolean) = ? 
} 
+0

Bạn đã có một cái nhìn tại Luồng? –

+0

Một luồng chắc chắn có thể phù hợp hơn ở đây trong trường hợp ví dụ, tuy nhiên tôi vẫn có cùng một vấn đề xung quanh cách tốt nhất để xây dựng phương thức mở rộng –

+2

Ồ, 'takeWhileInclusive'. [Cũ takeTo'] của tôi (https://issues.scala-lang.org/browse/SI-2963) .... –

Trả lời

7

Đây là một trường hợp mà tôi tìm ra giải pháp có thể thay đổi vượt trội:

class InclusiveIterator[A](ia: Iterator[A]) { 
    def takeWhileInclusive(p: A => Boolean) = { 
    var done = false 
    val p2 = (a: A) => !done && { if (!p(a)) done=true; true } 
    ia.takeWhile(p2) 
    } 
} 
implicit def iterator_can_include[A](ia: Iterator[A]) = new InclusiveIterator(ia) 
+0

Đây chắc chắn là một giải pháp thanh lịch cho vấn đề của tôi, cổ vũ! –

+0

Tôi sẽ lấy phiên bản chức năng với 'var' cũng không 'val', cảm ơn! –

+0

@oxbow_lakes - Nếu bạn không nhớ thêm chi phí, đó là một lựa chọn tốt. (Thông thường tôi sẽ không sử dụng một val cho chức năng này, tôi chỉ đang cố gắng tách riêng mọi thứ ra cho rõ ràng ở đây.) –

0
scala> List(0,1,2,3,4,5,6,7).toStream.filter (_ < 6).take(2) 
res8: scala.collection.immutable.Stream[Int] = Stream(0, ?) 

scala> res8.toList 
res9: List[Int] = List(0, 1) 

Sau khi cập nhật của bạn:

scala> def timeConsumeDummy (n: Int): Int = { 
    | println ("Time flies like an arrow ...") 
    | n } 
timeConsumeDummy: (n: Int)Int 

scala> List(0,1,2,3,4,5,6,7).toStream.filter (x => timeConsumeDummy (x) < 6) 
Time flies like an arrow ... 
res14: scala.collection.immutable.Stream[Int] = Stream(0, ?) 

scala> res14.take (4).toList 
Time flies like an arrow ... 
Time flies like an arrow ... 
Time flies like an arrow ... 
res15: List[Int] = List(0, 1, 2, 3) 

timeConsumeDummy được gọi là 4 lần. Tui bỏ lỡ điều gì vậy?

+0

Xin lỗi ví dụ không phải là trường hợp cụ thể tôi đang tìm cách giải quyết, tôi sẽ bao gồm một ví dụ sâu hơn để minh họa cho những gì tôi sau –

+0

@JPullar: Việc lấy (2) của bạn đã biến mất và thay đổi vị trí bằng (_ <6), trong khi timeConsumingMethod ở bên trái (_ <6) ngay bây giờ. Vì vậy, hiện (timeConsumingMethod) sản xuất một Int là kết quả, được so sánh với (_ <6) bây giờ, hoặc nó là yếu tố danh sách ban đầu, mà phải dưới 6? –

+0

Nội dung hiển thị của bạn là chính xác và những gì tôi có trong đánh giá lười biếng.Tuy nhiên, vấn đề của tôi là mô phỏng cách hàm lọc đánh giá một cách lười biếng theo phương thức mở rộng tùy chỉnh –

10

Bạn có thể sử dụng phương pháp span của Iterator để làm điều này khá sạch:

class IteratorExtension[A](i : Iterator[A]) { 
    def takeWhileInclusive(p: A => Boolean) = { 
    val (a, b) = i.span(p) 
    a ++ (if (b.hasNext) Some(b.next) else None) 
    } 
} 

object ImplicitIterator { 
    implicit def extendIterator[A](i : Iterator[A]) = new IteratorExtension(i) 
} 

import ImplicitIterator._ 

Bây giờ (0 until 10).toIterator.takeWhileInclusive(_ < 4).toList cho List(0, 1, 2, 3, 4), ví dụ.

+1

Dòng cuối cùng của phương thức của bạn có thể được viết gọn gàng hơn như 'a ++ (b take 1)' –

2
class IteratorExtension[T](i : Iterator[T]) { 
    def takeWhileInclusive(predicate:(T) => Boolean) = new Iterator[T] { 
    val it = i 
    var isLastRead = false 

    def hasNext = it.hasNext && !isLastRead 
    def next = { 
     val res = it.next 
     isLastRead = !predicate(res) 
     res 
    } 
    } 
} 

Và có lỗi trong tiềm ẩn của bạn. Ở đây nó được cố định:

object ImplicitIterator { 
    implicit def extendIterator[T](i : Iterator[T]) = new IteratorExtension(i) 
} 
+0

Chà, có một số lỗi nghiêm trọng trong phiên bản trước của tôi. –

+0

Đây là nơi mà suy nghĩ của tôi đang hướng đến, cảm ơn bạn đã đến đó vì tôi! Nó cung cấp một cách tiếp cận tổng quát tốt. Tôi ước có một giải pháp tổng quát thanh lịch hơn sau đó phải xây dựng một trình lặp mới. –

3

Sau đây đòi hỏi scalaz để có được fold trên một tuple (A, B)

scala> implicit def Iterator_Is_TWI[A](itr: Iterator[A]) = new { 
    | def takeWhileIncl(p: A => Boolean) 
    | = itr span p fold (_ ++ _.toStream.headOption) 
    | } 
Iterator_Is_TWI: [A](itr: Iterator[A])java.lang.Object{def takeWhileIncl(p: A => Boolean): Iterator[A]} 

đây nó đang hoạt động:

scala> List(1, 2, 3, 4, 5).iterator takeWhileIncl (_ < 4) 
res0: Iterator[Int] = non-empty iterator 

scala> res0.toList 
res1: List[Int] = List(1, 2, 3, 4) 

Bạn có thể cuộn gấp của riêng bạn trên một cặp như thế này:

scala> implicit def Pair_Is_Foldable[A, B](pair: (A, B)) = new { 
    | def fold[C](f: (A, B) => C): C = f.tupled(pair) 
    | } 
Pair_Is_Foldable: [A, B](pair: (A, B))java.lang.Object{def fold[C](f: (A, B) => C): C}