5

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

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

4 回答 4

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))

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

于 2010-02-08T19:36:23.027 回答
2

我对它在 Scala 2.8 上运行做了一些改变(主要是,fromPath而不是fromFile,和()之后getLines)。它可能使用了一些 Scala 2.8 特性,尤其是groupBy. 可能toSet也是如此,但在 2.7 上很容易适应。

我没有文件来测试它,但我将这些东西从 更改valdef,并且类型签名至少匹配。

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
)
于 2010-02-08T20:03:01.507 回答
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)
于 2010-02-08T17:05:48.003 回答
0

这里的一种选择是按照上述方式设置一些可变但瞬态的配置器类,MutableMap然后以某种不可变的形式将其传递给您的实际类:

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

然后当然你可以readMutableJobs按照你已经编码的方式实现

于 2010-02-08T16:10:32.660 回答