как считать неизменные структуры данных из файла в scala

Мне сделали структуру данных 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
}

Заранее спасибо за каждое предложение!

5
задан Filippo Tabusso 8 February 2010 в 16:15
поделиться

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])), а затем обработать условие ошибки, когда задание list содержал запись, которой не было в списке заданий. (Вам все равно придется обновлять карту списка заданий каждый раз, когда вы читаете новую задачу.)

4
ответ дан 14 December 2019 в 08:49
поделиться

Я сделал некоторые изменения, чтобы он работал на 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
ответ дан 14 December 2019 в 08:49
поделиться

Вы всегда можете отложить создание объекта, пока не прочитаете все данные из файла, например:

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)
1
ответ дан 14 December 2019 в 08:49
поделиться

Один из вариантов здесь - иметь некоторый изменяемый, но переходный класс конфигурации в соответствии со строками MutableMap выше, но затем передайте это в некоторой неизменяемой форме вашему фактическому классу:

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

Затем, конечно, вы можете реализовать readMutableJobs в соответствии со строками, которые вы уже закодировали

0
ответ дан 14 December 2019 в 08:49
поделиться
Другие вопросы по тегам:

Похожие вопросы: