2010-02-08 31 views
5

我有一個由作業組成的數據結構,每個數據結構都包含一組任務。無論工作和任務數據在文件中定義這樣的:如何從scala中的文件讀取不可變數據結構

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

的最有效的方式做到這將是閱讀一切都變成可變的結構,然後轉換爲不可變的人在最後,但是這可能需要上課了很多冗餘編碼與很多領域。相反,請考慮使用底層集合使用的相同模式:具有新任務的作業是新作業

下面是一個甚至不打擾閱讀作業列表的例子 - 它從任務列表中推斷它。 (這是在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])),然後處理任務列表包含不在作業列表中的條目的錯誤情況。 (您仍需要每次在一個新的任務讀取時間更新作業列表地圖。)

+0

我喜歡這種方法。但是我只寫了一個'addTask'方法,它返回一個新的'Job'和相同的數據,再加上新的任務。它會稍微改變邏輯,但是,就像這樣,'Job'似乎對如何初始化太多瞭解太多。 :-) –

+0

我這樣做是爲了突出舊工作的替代,這在我看來是這裏的關鍵概念。但我同意某個地方的addTask會更好。有很多地方可以爭論(應該採用'Option [Job]',還是圍繞可變映射關閉?)。 –

+0

謝謝,我喜歡這個創建新Job的Job的想法的解決方案(通過構造函數或addTask方法)。我仍然對scala很陌生(我來自java),但我不確定在這種情況下,不變性是否值得創建許多對象,因爲對我而言,性能非常重要(在真正的情況下,我有更多的2類,與他們和上千個對象之間的複雜鏈接)。 –

0

一種選擇是有一些可變的,但沿上方,但隨後MutableMap的線瞬態配置者階級通過一些一成不變的形式它傳遞給你的實際等級:

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

隨後的當然你可以沿着你已經編碼的線實現readMutableJobs

+0

對不起,我was'n足夠清晰:作業地圖是確定是可變的,它是任務設置單工作,這應該是一成不變的範圍內(我已經編輯我的問題) –

+0

我認爲這是公平地說,你可以對可變/不可變任務使用同樣的方法,因爲它已經在作業圖上工作了!例如,有一個'Job'構造函數創建任務的不可變副本 –

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

謝謝。對於我發佈的示例,您的解決方案是可以的,但在實際情況下,Job和Task的字段不僅僅是id。例如,Job也有一個截止日期(日期),任務有一個長度(Int),等等...... –

+0

再次感謝,這是我第一次遇到問題時想到的解決方案。然而,在我看來,它需要太多額外的代碼,這意味着更多的錯誤,維護,... –

1

我做了的感覺變化爲它在斯卡拉2.8運行(主要是,fromPath代替fromFilegetLines()) 。它可能使用了一些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 
) 
相關問題