将while循环更改为Stream但缺少最后一个元素

时间:2013-07-21 14:45:38

标签: scala

我试图用Stream替换我的while循环但是当我使用Stream版本时,最后一个元素总是丢失。我想我明白为什么它会丢失,但我无法弄清楚如何解决这个问题。有什么建议吗?还有更好的方法吗?感谢

使用Stream:

if(cursor.moveToFirst) {
val result = Stream.continually((
  Song(
    cursor.getLong(c(BaseColumns._ID)),
    cursor.getString(c(MediaColumns.TITLE))     
  ),
  cursor.moveToNext))
.takeWhile(_._2)
.map(_._1)
result.toList
}

这是while循环版本正常工作。

cursor.moveToFirst()
var i = 0
var list: List[Song] = List.empty
while (i < cursor.getCount()) {
  val s = Song(
    cursor.getLong(c(BaseColumns._ID)),
    cursor.getString(c(MediaColumns.TITLE)))
  i = i + 1
  list = list :+ s
  cursor.moveToNext()

注意:

使用MoveToNext

将光标移动到下一行。 如果光标已经超过结果集中的最后一个条目,则此方法将返回false。

2 个答案:

答案 0 :(得分:2)

假设您有一个条目。你产生了元组

(song, false)

扔掉它。这显然不好。即使那里没有另一首歌曲你想要保留这首歌!所以你可以试试一个选项 - 如果有的话加载一首歌,如果没有则加载。您还必须区别对待第一个元素,因为moveToFirstmoveToNext不同。像这样:

def loadSong = Song(
  cursor.getLong(c(BaseColumns._ID)),
  cursor.getString(c(MediaColumns.TITLE))     
)

if (cursor.moveToFirst) {
  val songs = Stream(Some(loadSong)) ++
    Stream.continually(if (cursor.moveToNext()) Some(loadSong) else None)
  songs.flatten.toList
}

请注意,您实际上并未在此处使用流的流畅性,因此您也可以使用Iterator.continually

注意:我可能会将包装逻辑移到someLong并生成类似的内容:

def loadSong(b: Boolean) = if (!b) None else Some(Song(
  cursor.getLong(c(BaseColumns._ID)),
  cursor.getString(c(MediaColumns.TITLE))     
))

val songs = Stream(loadSong(cursor.moveToFirst())) ++
    Stream.continually(loadSong(cursor.moveToNext())
songs.flatten.toList

或者,根据我个人图书馆的方法:

def optIn[A](b: Boolean)(a: => A) = if (b) Some(a) else None

来自之前的普通loadSong

val songs = Stream(optIn(cursor.moveToFirst)(loadSong)) ++
  Stream.continually(optIn(cursor.moveToNext)(loadSong))
songs.flatten.toList

可能是最紧凑和可读的。 (一旦你知道如何阅读optIn。)

答案 1 :(得分:1)

你的光标是一种迭代器,不太方便。因此,作为Rex Kerr答案的替代方案,您可以抽象出cursorIterator(或Stream)之间的整个转换,这样您就可以像对待光标一样迭代这样:

def loadSong(c: Cursor) = (
  c.getLong(c(BaseColumns._ID)),
  c.getString(c(MediaColumns.TITLE))     
)

cursor.map(loadSong).toList

为此,您只需要从CursorIterator[Cursor]的隐式转换,其中光标在每次迭代时处于正确状态(准备读取)。实施可以是:

implicit def cursor2Iterator(c: Cursor): Iterator[Cursor] = if (c.moveToFirst) 
  new NonEmptyCursorIterator(c) else Iterator.empty

sealed class NonEmptyCursorIterator(private val c: Cursor) extends Iterator[Cursor] {
    private[this] var hasNext0 = true // always has at least one element

    def hasNext() = {
      if (!hasNext0) hasNext0 = c.moveToNext
      hasNext0
    }

    def next(): Cursor = if (hasNext()) {
      hasNext0 = false 
      c
    } else throw new NoSuchElementException("hasNext() is false")
}

这使得代码简短易读。