2010-02-08 30 views
5

Tôi có cấu trúc dữ liệu được thực hiện bởi mỗi công việc chứa một tập hợp các tác vụ. Cả hai công việc và nhiệm vụ dữ liệu được định nghĩa trong các tập tin như thế này:cách đọc cấu trúc dữ liệu bất biến từ tệp trong scala

jobs.txt: 
JA 
JB 
JC 

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

Quá trình tạo các đối tượng như sau:
- đọc từng công việc, tạo ra nó và lưu trữ nó bằng id
- đọc nhiệm vụ, lấy công việc theo id, tạo tác vụ, lưu trữ tác vụ trong công việc

Khi tệp được đọc cấu trúc dữ liệu này không bao giờ được sửa đổi. Vì vậy, tôi muốn các nhiệm vụ đó trong công việc sẽ được lưu trữ trong một tập hợp bất biến. Nhưng tôi không biết làm thế nào để làm điều đó một cách hiệu quả. (Lưu ý: bản đồ bất biến lưu trữ công việc có thể để bất biến)

Đây là một phiên bản đơn giản của các mã:

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 
} 

Cảm ơn trước cho mỗi gợi ý!

Trả lời

4

Cách hiệu quả nhất để làm điều này sẽ được đọc mọi thứ thành những cấu trúc có thể thay đổi và sau đó chuyển đổi cho những người không thể thay đổi ở cuối, nhưng điều này có thể đòi hỏi nhiều mã dư thừa cho các lớp học với rất nhiều lĩnh vực. Vì vậy, thay vào đó, hãy xem xét sử dụng cùng một mẫu mà bộ sưu tập cơ bản sử dụng: công việc có nhiệm vụ mới là công việc mới.

Đây là một ví dụ thậm chí không bận tâm khi đọc danh sách công việc - nó đưa nó vào danh sách nhiệm vụ. (Đây là một ví dụ mà làm việc dưới 2.7.x; phiên bản gần đây của 2.8 sử dụng "Source.fromPath" thay vì "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)) 

Một cách tiếp cận thay thế sẽ đọc danh sách công việc (như giải quyết việc làm Công việc mới (jobID, Set.empty [Tác vụ])), và sau đó xử lý các điều kiện lỗi khi danh sách nhiệm vụ chứa một mục nhập không có trong danh sách công việc. (Bạn vẫn sẽ cần phải cập nhật bản đồ danh sách công việc mỗi khi bạn đọc trong một nhiệm vụ mới.)

+0

Tôi thích cách tiếp cận này. Nhưng tôi chỉ viết một phương thức 'addTask' trả về một' Job' mới với cùng dữ liệu, cộng với nhiệm vụ mới. Nó sẽ thay đổi logic một chút, nhưng, vì nó là, 'Công việc' dường như biết quá nhiều về cách nó sẽ được khởi tạo. :-) –

+0

Tôi đã làm theo cách này để làm nổi bật việc thay thế công việc cũ bằng một công việc mới, dường như tôi là khái niệm then chốt ở đây. Nhưng tôi đồng ý rằng một 'addTask' ở đâu đó sẽ tốt hơn. Có rất nhiều nơi mà người ta có thể tranh luận (nên lấy một 'Lựa chọn [Công việc]', hoặc là một đóng cửa quanh bản đồ có thể thay đổi được?). –

+0

Cảm ơn, tôi thích giải pháp này cho ý tưởng của công việc tạo công việc mới (bằng cách xây dựng hoặc phương pháp addTask). Tôi vẫn còn rất mới để scala (Tôi đến từ java) và tôi không chắc chắn nếu, trong một trường hợp như thế này, bất biến là giá trị của việc có nhiều sáng tạo đối tượng kể từ khi tôi thực hiện là khá quan trọng (trong trường hợp thực sự tôi có nhiều hơn 2 lớp, với các liên kết phức tạp giữa chúng và hàng nghìn đối tượng). –

0

Một lựa chọn ở đây là để có một số có thể thay đổi nhưng thoáng qua lớp configurer dọc theo dòng của MutableMap trên nhưng sau đó chuyển thông tin này thông qua trong một số hình thức bất biến đến lớp thực tế của bạn:

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

Sau đó các bạn có thể thực hiện readMutableJobs dọc theo các dòng bạn đã mã hóa

+0

Xin lỗi tôi was'n rõ ràng đủ: Bản đồ công ăn việc làm là ok là có thể thay đổi, đó là nhiệm vụ Đặt trong công việc duy nhất mà nên bất biến (Tôi đã chỉnh sửa câu hỏi của tôi) –

+0

Tôi nghĩ đó là công bằng khi nói bạn có thể làm cho phương pháp tương tự hoạt động trên các tác vụ có thể thay đổi/không thay đổi được vì nó đã làm việc trên bản đồ công việc! Ví dụ, có một hàm tạo 'Job' lấy một bản sao bất biến của các tác vụ khi nó được tạo ra –

1

Bạn luôn có thể trì hoãn việc tạo đối tượng cho đến khi bạn có tất cả dữ liệu được đọc từ tệp, như:

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

Để đối phó với nhiều lĩnh vực, bạn có thể (với một số nỗ lực) tạo ra một phiên bản có thể thay đổi các lớp học bất biến của bạn, sử dụng cho tòa nhà. Sau đó, chuyển đổi khi cần thiết:

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

Cảm ơn. Giải pháp của bạn là ok cho ví dụ tôi đăng, nhưng trong trường hợp thực sự cả Công việc và Nhiệm vụ đều có nhiều trường hơn chỉ là các id. Ví dụ công việc cũng có ngày hết hạn (Ngày) và Nhiệm vụ có độ dài (Int), v.v ... –

+0

Cảm ơn một lần nữa, đây là giải pháp tôi nghĩ đến khi tôi gặp vấn đề lần đầu tiên. Tuy nhiên, theo ý kiến ​​của tôi, nó đòi hỏi quá nhiều mã phụ có nghĩa là nhiều lỗi, bảo trì, ... –

1

Tôi đã làm một sự thay đổi cảm giác về nó để chạy trên Scala 2.8 (chủ yếu là, fromPath thay vì fromFile, và () sau getLines) . Nó có thể sử dụng một vài tính năng Scala 2.8, đáng chú ý nhất là groupBy. Có lẽ là toSet là tốt, nhưng đó là một trong những dễ dàng để thích ứng trên 2,7.

Tôi không có tệp để kiểm tra, nhưng tôi đã thay đổi nội dung này từ val thành def và các chữ ký loại, ít nhất, khớp.

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 
) 
Các vấn đề liên quan