2012-02-17 153 views
10

即時通訊擴展迭代器來創建一個新的方法takeWhileInclusive,它將像takeWhile一樣運行,但包括最後一個元素。斯卡拉,擴展迭代器

我的問題是什麼是最好的做法來擴展迭代器返回一個新的迭代器,我想懶惰評估。從C#背景我正常使用IEnumerable並使用yield關鍵字,但這樣的選項似乎不存在於斯卡拉。

例如我可能有

List(0,1,2,3,4,5,6,7).iterator.map(complex time consuming algorithm).takeWhileInclusive(_ < 6) 
在這種情況下, takeWhileInclusive只會有解決的值謂詞

,直到我得到比6的結果時,它會包括這第一個結果

到目前爲止我有:

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

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

你不得不看看流? – 2012-02-17 14:34:32

+0

在這個例子中,一個流肯定會更合適,但是我仍然有關於如何最好地構建擴展方法的相同問題 – 2012-02-17 14:40:00

+2

哦,'takeWhileInclusive'。我的舊['takeTo'](https://issues.scala-lang.org/browse/SI-2963).... – 2012-02-17 15:17:20

回答

7

這就是我的上級可變解決一個案例:

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

這絕對是一個優雅的解決方案,我的問題,歡呼! – 2012-02-17 17:16:44

+0

我將採用功能版本既不'var也不'val',謝謝! – 2012-02-17 20:35:29

+0

@oxbow_lakes - 如果你不介意額外的開銷,這是一個很好的選擇。 (通常我不會使用val來實現這個功能;我只是爲了清晰起見而將其分開) – 2012-02-17 21:06:28

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) 

您更新後:

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被調用4次。我錯過了什麼嗎?

+0

對不起,這個例子不是我想要解決的具體情況,我將包括一個更深入的例子來說明我在 – 2012-02-17 14:41:41

+0

@JPullar之後的情況:現在,您的take(2)已經消失並且以(_ <6)更改了位置,而timeConsumingMethod現在位於(_ <6)的左側。那麼(timeConsumingMethod)是否會產生一個Int作爲結果,現在將它與(_ <6)進行比較,還是它是初始List元素,它必須低於6? – 2012-02-17 16:54:45

+0

你的展示是否正確,以及我在懶惰評估中的結果。然而,我的問題是模擬過濾器函數如何在自定義擴展方法中懶惰地評估 – 2012-02-17 17:12:49

10

您可以使用Iteratorspan方法要做到這一點非常乾淨:

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._ 

現在(0 until 10).toIterator.takeWhileInclusive(_ < 4).toListList(0, 1, 2, 3, 4),例如。

+1

您的方法的最後一行可以更簡潔地寫成'a ++(b take 1)' – 2012-02-18 17:33:25

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

而你的隱含有一個錯誤。這是固定的:

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

哇,在我以前的版本中有一些嚴重的錯誤。 – 2012-02-17 15:36:32

+0

這就是我的想法所在,感謝您爲我而來!它提供了一個很好的通用方法。我希望有一個更優雅的通用解決方案,然後不得不構建一個新的迭代器。 – 2012-02-17 17:16:13

3

以下要求scalaz對一個元組得到fold(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]} 

這裏它在工作:

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) 

你可以滾你自己折了一對這樣的:

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}