如何从scala中的文件中读取不可变数据结构

时间:2010-02-08 16:04:37

标签: data-structures scala immutability mutable

我有一个由Jobs组成的数据结构,每个都包含一组Tasks。作业和任务数据都在以下文件中定义:

jobs.txt:
JA
JB
JC

tasks.txt:
JB  T2
JA  T1
JC  T1
JA  T3
JA  T2
JB  T1 

创建对象的过程如下:
- 阅读每个作业,创建它并按照id存储它 - 读取任务,按ID检索作业,创建任务,在作业中存储任务

读取文件后,永远不会修改此数据结构。所以我希望作业中的任务存储在一个不可变的集合中。但我不知道如何以有效的方式做到这一点。 (注意:存储作业的不可变映射可能不可变)

以下是代码的简化版本:

class Task(val id: String) 

class Job(val id: String) {
    val tasks = collection.mutable.Set[Task]() // This sholud be immutable
}

val jobs = collection.mutable.Map[String, Job]() // This is ok to be mutable

// read jobs
for (line <- io.Source.fromFile("jobs.txt").getLines) { 
    val job = new Job(line.trim)
    jobs += (job.id -> job)
}

// read tasks
for (line <- io.Source.fromFile("tasks.txt").getLines) {
    val tokens = line.split("\t")
    val job = jobs(tokens(0).trim)
    val task = new Task(job.id + "." + tokens(1).trim)
    job.tasks += task
}

提前感谢您的每一个建议!

4 个答案:

答案 0 :(得分:4)

执行此操作的最有效方法是将所有内容读入可变结构,然后在最后转换为不可变结构,但这可能需要对具有大量字段的类进行大量冗余编码。因此,请考虑使用与基础集合使用的模式相同的模式:具有新任务的作业是新作业

这是一个甚至懒得阅读作业列表的例子 - 它从任务列表中推断出来。 (这是一个在2.7.x下工作的示例; 2.8的最新版本使用“Source.fromPath”而不是“Source.fromFile”。)

object Example {
  class Task(val id: String) {
    override def toString = id
  }

  class Job(val id: String, val tasks: Set[Task]) {
    def this(id0: String, old: Option[Job], taskID: String) = {
      this(id0 , old.getOrElse(EmptyJob).tasks + new Task(taskID))
    }
    override def toString = id+" does "+tasks.toString
  }
  object EmptyJob extends Job("",Set.empty[Task]) { }

  def read(fname: String):Map[String,Job] = {
    val map = new scala.collection.mutable.HashMap[String,Job]()
    scala.io.Source.fromFile(fname).getLines.foreach(line => {
      line.split("\t") match {
        case Array(j,t) => {
          val jobID = j.trim
          val taskID = t.trim
          map += (jobID -> new Job(jobID,map.get(jobID),taskID))
        }
        case _ => /* Handle error? */
      }
    })
    new scala.collection.immutable.HashMap() ++ map
  }
}

scala> Example.read("tasks.txt")
res0: Map[String,Example.Job] = Map(JA -> JA does Set(T1, T3, T2), JB -> JB does Set(T2, T1), JC -> JC does Set(T1))

另一种方法是读取作业列表(创建作业作为新作业(jobID,Set.empty [Task])),然后处理任务列表包含不在作业中的条目的错误条件名单。 (每次阅读新任务时,您仍需要更新作业列表地图。)

答案 1 :(得分:2)

我确实感觉它在Scala 2.8上运行(主要是fromPath而不是fromFile()之后getLines。它可能使用了一些Scala 2.8功能,最值得注意的是groupBy。也可能是toSet,但这个很容易适应2.7。

我没有测试它的文件,但是我将这些内容从val更改为def,并且类型签名至少匹配。

class Task(val id: String)  
class Job(val id: String, val tasks: Set[Task])

// read tasks 
val tasks = (
  for {
    line <- io.Source.fromPath("tasks.txt").getLines().toStream
    tokens = line.split("\t") 
    jobId = tokens(0).trim
    task = new Task(jobId + "." + tokens(1).trim) 
  } yield jobId -> task
).groupBy(_._1).map { case (key, value) => key -> value.map(_._2).toSet }

// read jobs 
val jobs = Map() ++ (
  for {
    line <- io.Source.fromPath("jobs.txt").getLines()
    job = new Job(line.trim, tasks(line.trim))
  } yield job.id -> job
)

答案 2 :(得分:1)

您可以随时延迟创建对象,直到您从文件中读入所有数据,例如:

case class Task(id: String) 
case class Job(id: String, tasks: Set[Task])

import scala.collection.mutable.{Map,ListBuffer}
val jobIds = Map[String, ListBuffer[String]]()

// read jobs
for (line <- io.Source.fromFile("jobs.txt").getLines) { 
    val job = line.trim
    jobIds += (job.id -> new ListBuffer[String]())
}

// read tasks
for (line <- io.Source.fromFile("tasks.txt").getLines) {
    val tokens = line.split("\t")
    val job = tokens(0).trim
    val task = job.id + "." + tokens(1).trim
    jobIds(job) += task
}

// create objects
val jobs = jobIds.map { j =>
    Job(j._1, Set() ++ j._2.map { Task(_) })
}

为了处理更多字段,您可以(通过一些努力)制作用于构建的不可变类的可变版本。然后,根据需要进行转换:

case class Task(id: String)
case class Job(val id: String, val tasks: Set[Task])
object Job {
    class MutableJob {
        var id: String = ""
        var tasks = collection.mutable.Set[Task]()
        def immutable = Job(id, Set() ++ tasks)
    }
    def mutable(id: String) = {
        val ret = new MutableJob
        ret.id = id
        ret
    }
}

val mutableJobs = collection.mutable.Map[String, Job.MutableJob]() 

// read jobs
for (line <- io.Source.fromFile("jobs.txt").getLines) { 
    val job = Job.mutable(line.trim)
    jobs += (job.id -> job)
}

// read tasks
for (line <- io.Source.fromFile("tasks.txt").getLines) {
    val tokens = line.split("\t")
    val job = jobs(tokens(0).trim)
    val task = Task(job.id + "." + tokens(1).trim)
    job.tasks += task
}

val jobs = for ((k,v) <- mutableJobs) yield (k, v.immutable)

答案 3 :(得分:0)

这里的一个选择是在上面MutableMap的行上有一些可变的瞬态配置类,但是然后通过一些不可变形式传递给它你的实际班级:

val jobs: immutable.Map[String, Job] = {
  val mJobs = readMutableJobs
  immutable.Map(mJobs.toSeq: _*)
}

当然,您可以沿着已编码的行

实施readMutableJobs