You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何无突变地条件式消费Scala的io.Source迭代器?

无突变实现Scala文件按条件分块读取

问题背景

需要基于scala.io.Source按条件分块读取文件,处理完一个块后,后续读取能从当前位置继续。现有实现依赖可变变量,希望找到无需突变源的更优方案,同时要注意Iterator.takeWhile无法满足需求——它会跳过第一个不满足条件的元素,导致数据丢失。

为什么takeWhile不可用

takeWhile会遍历迭代器直到遇到第一个不满足条件的元素,但这个元素会被直接跳过,不会留在迭代器中供后续读取。比如下面的示例中,数字5就丢失了:

val it = List(1,2,3,4,5,6,7,8,9).iterator
val res1 = it.takeWhile(_<5).toList
// List(1, 2, 3, 4)
val res2 = it.takeWhile(_<10).toList
// List(6, 7, 8, 9), 5 is missing

现有可变实现

通过类内部的可变变量保存迭代器状态,每次调用get方法时用span拆分满足条件的元素和剩余迭代器,再更新可变变量:

import scala.io.Source

class Reader(filePath: String) {
    var src = Source.fromFile(filePath).getLines
    def get(f: String => Boolean) = {
        val (a, b) = src.span(f)
        src = b
        a
    }
}

object Reader {
    def apply(filePath: String) = new Reader(filePath)
}

val file = Reader("file")

val res1 = file.get(_.toInt < 5).toList
// List("0", "1", "2", "3", "4")
val res2 = file.get(_.toInt < 10).toList
// List("5", "6", "7", "8", "9")

无突变的纯函数式实现

可以用case class封装迭代器状态,每次调用get方法时返回匹配结果和包含剩余迭代器的新实例,全程避免可变变量:

import scala.io.Source

case class PureReader(src: Iterator[String]) {
  def get(f: String => Boolean): (List[String], PureReader) = {
    val (matching, remaining) = src.span(f)
    (matching.toList, PureReader(remaining))
  }
}

object PureReader {
  def apply(filePath: String): PureReader = 
    PureReader(Source.fromFile(filePath).getLines())
}

// 使用示例
val initialReader = PureReader("file")
val (res1, readerAfterRes1) = initialReader.get(_.toInt < 5)
// res1: List("0", "1", "2", "3", "4")
val (res2, _) = readerAfterRes1.get(_.toInt < 10)
// res2: List("5", "6", "7", "8", "9")

实现说明

  • span方法会将迭代器拆分为两部分:前缀(所有满足条件的元素)和剩余部分(从第一个不满足条件的元素开始),完美解决了takeWhile丢失元素的问题。
  • 每次调用get都返回新的PureReader实例,没有可变状态,符合函数式编程的无副作用原则,同时保证了读取位置的连续性。

内容的提问来源于stack exchange,提问作者Sergey Bushmanov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:08:39