Мне сделали структуру данных Jobs каждый содержащий ряд Задач. И данные Задания и Задачи определяются в файлах как они:
jobs.txt:
JA
JB
JC
tasks.txt:
JB T2
JA T1
JC T1
JA T3
JA T2
JB T1
Процесс создания объектов следующий:
- считайте каждое задание, создайте его и сохраните его идентификатором
- считайте задачу, получите задание идентификатором, создайте задачу, сохраните задачу в задании
После того как файлы читаются, эта структура данных никогда не изменяется. Таким образом, я хотел бы это, задачи в заданиях будут сохранены в неизменном наборе. Но я не знаю, как сделать это эффективным способом. (Отметьте: неизменную карту, хранящую задания, можно оставить неизменной),
Вот упрощенная версия кода:
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
}
Заранее спасибо за каждое предложение!
Самый эффективный способ сделать это - прочитать все в изменяемые структуры, а затем преобразовать в неизменяемые в конце, но для этого может потребоваться много избыточное кодирование для классов с большим количеством полей. Поэтому вместо этого рассмотрите возможность использования того же шаблона, что и базовая коллекция: задание с новой задачей - это новое задание .
Вот пример, который даже не утруждает себя чтением списка заданий - он выводит его из списка задач. (Это пример, который работает под 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])), а затем обработать условие ошибки, когда задание list содержал запись, которой не было в списке заданий. (Вам все равно придется обновлять карту списка заданий каждый раз, когда вы читаете новую задачу.)
Я сделал некоторые изменения, чтобы он работал на 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
)
Вы всегда можете отложить создание объекта, пока не прочитаете все данные из файла, например:
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)
Один из вариантов здесь - иметь некоторый изменяемый, но переходный класс конфигурации в соответствии со строками MutableMap
выше, но затем передайте это в некоторой неизменяемой форме вашему фактическому классу:
val jobs: immutable.Map[String, Job] = {
val mJobs = readMutableJobs
immutable.Map(mJobs.toSeq: _*)
}
Затем, конечно, вы можете реализовать readMutableJobs
в соответствии со строками, которые вы уже закодировали