2012-02-17 78 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}