Pregunta

Tengo una estructura de datos hecha de Empleo cada uno con un conjunto de tareas. Tanto los datos de empleo y de tareas se definen en archivos como estos:

jobs.txt:
JA
JB
JC

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

El proceso de creación de objetos es la siguiente:
- leer cada puesto de trabajo, crear y almacenarlo por id
- tarea de lectura, recuperar el trabajo por id, crear la tarea, la tarea tienda en el trabajo

Una vez que se leen los archivos de esta estructura de datos no se modifica. Así que me gustaría que las tareas dentro de puestos de trabajo se almacenan en un conjunto inmutable. Pero no sé cómo hacerlo de una manera eficiente. (Nota: el mapa inmutable almacenamiento de trabajos se puede dejar inmutable)

Esta es una versión simplificada del código:

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
}

Gracias de antemano por cualquier sugerencia!

¿Fue útil?

Solución

La forma más eficiente de hacer esto sería leer todo en estructuras mutables y luego convertir a los inmutables al final, pero esto podría requerir una gran cantidad de codificación redundante para las clases con una gran cantidad de campos. Así que en lugar, puede utilizar el mismo patrón que la colección subyacente utiliza:. Un trabajo con una nueva tarea es un trabajo nueva

Este es un ejemplo de que ni siquiera se molestó en leer la lista de puestos de trabajo - se infiere de la lista de tareas. (Este es un ejemplo que funciona bajo 2.7.x;. Versiones recientes de 2.8 uso "Source.fromPath" en lugar de "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))

Un enfoque alternativo sería leer la lista de tareas (la creación de puestos de trabajo como trabajo nuevo (de la Id, Set.empty [tarea])), y luego manejar la condición de error cuando la lista de tareas contiene una entrada que no estaba en el trabajo lista. (Usted todavía tiene que actualizar el mapa lista de trabajos cada vez que se lee en una nueva tarea.)

Otros consejos

Hice una sensación cambia para que se ejecute en Scala 2.8 (en su mayoría, en lugar de fromPath fromFile, y después () getLines). Puede ser el uso de un par de Scala 2.8 características, más notablemente groupBy. Probablemente toSet así, pero eso es fácil de adaptar en 2.7.

No tengo los archivos para probarlo, pero he cambiado esto desde val a def, y las firmas de tipos, al menos, un partido.

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
)

Siempre se puede retrasar la creación de objetos hasta que tenga todos los datos leídos desde el archivo, como:

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(_) })
}

Para hacer frente a más campos, usted podría (con un poco de esfuerzo) hacer una versión mutable de sus clases inmutables, que se utilizan para la construcción. A continuación, convertir, según sea necesario:

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)

Una opción aquí es tener algún mutable pero transitoria clase configurador lo largo de las líneas de la MutableMap anterior pero luego pasar esta a través de alguna forma inmutable a su clase real:

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

Entonces, por supuesto, se puede implementar readMutableJobs lo largo de las líneas que ya han codificadas

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top