在Scala中将数据从文件读取到不可变的二维数组的简洁方法

时间:2011-02-17 14:41:29

标签: scala multidimensional-array io immutability

我正在寻找的是一种简洁的方式,最终得到一个不可变的二维数组 X 和一维数组 Y ,而不先扫描文件找出数据的维度。

由标题行后跟列式双精度值组成的数据采用以下格式

X0, X1, X2, ...., Y
0.1, 1.2, -0.2, ..., 1.1
0.2, 0.5, 0.4, ..., -0.3
-0.5, 0.3, 0.3, ..., 0.1

我有以下代码(到目前为止)从文件中获取行并标记每个逗号分隔行以获取样本。它目前不会填写 X Y 数组,也不会分配 num dimx

val X = new Array[Array[Double]](num,dimx)
val Y = new Array[Double](num)

def readDataFromFile(filename: String) {
    var firstTime = true
    val lines = fromFile(filename).getLines
    lines.foreach(line => {
        val tokens = line split(",")
        if(firstTime) {
            tokens.foreach(token => // get header titles and set dimx)
            firstTime = false
        } else {
            println("data")
            tokens.foreach(token => //blah, blah, blah...)
        }
    })
}

显然这是一个问题,因为虽然我可以即时检测并使用 dimx ,但我不知道 num 先验。此外,重复的tokens.foreach不是很优雅。我可以先扫描文件并确定尺寸,但这似乎是一种令人讨厌的方式。有没有更好的办法?提前致谢

3 个答案:

答案 0 :(得分:3)

没有任何内置功能可以告诉您数据的大小。为什么不让方法返回你的数组而不是你在外面声明它们?这样你就可以更好地处理错误情况。

case class Hxy(headers: Array[String], x: Array[Array[Double]], y: Array[Double]) {}
def readDataFromFile(name: String): Option[Hxy] = {
  val lines = io.Source.fromFile(name).getLines
  if (!lines.hasNext) None
  else {
    val header = lines.next.split(",").map(_.trim)
    try {
      val xy = lines.map(_.split(",").map(_.trim.toDouble)).toArray
      if (xy.exists(_.length != header.length)) None
      else Some( Hxy(header, xy.map(_.init), xy.map(_.last)) )
    }
    catch { case nfe: NumberFormatException => None }
  }
}

在这里,只有我们拥有格式良好的数据,我们才能获得相关数组(有帮助地打包到案例类中);否则,我们会回来None,所以我们知道出了问题。

(如果您想知道它为什么不起作用,请将Option[Hxy]替换为Either[String,Hxy],并在成功时返回Right(...)而不是Some(...),{{1失败时代替Left(message)。)


编辑:如果您希望值(不仅仅是数组大小)是不可变的,那么您需要将所有内容映射到某处的None。当您将数据放入Vector时,我可能会在最后一步执行此操作。

答案 1 :(得分:0)

Array,与Java一样可变。所以你不能拥有不可变数组。你需要在Array和immutablity之间做出选择。一种方法是,如何在没有foreach es和var的情况下实现目标与以下内容类似:

// simulate the lines for this example
val lines = List("X,Y,Z,","1,2,3","2,5.0,3.4") 
val res = lines.map(_.split(",")).toArray

答案 2 :(得分:0)

使用Array.newBuilder。我假设标题已被提取。

val b = Array.newBuilder[Array[Double]]
lines.foreach { b += _.split(",").map(_.toDouble) }
val data = b.result

如果您想成为不可变的,请采用IndexedSeq(例如Vector)而不是Array的一些不可变实现;建设者致力于所有馆藏。