2012-01-22 22 views
21

Tôi đang loay hoay xung quanh vào chiều Chủ Nhật và đang cố gắng tạo ra một cấu trúc 'phòng' các loại. Về cơ bản, một đối tượng Room có một số lần thoát, mỗi mục tham chiếu đến Room giây khác. Bây giờ, điều đầu tiên tôi đang cố gắng tạo ra là hai số Room được kết nối với nhau, tốt nhất là trong một câu lệnh gán. Như thế này:Làm cách nào để tôi tham chiếu đến biến trong khi gán giá trị cho biến đó trong khi vẫn giữ lại bất biến?

case class Room(title: String, exits: Map[Direction.Direction, Room]) 

val firstRoom = Room("A room", Map(North -> Room("Another room", Map(South -> firstRoom)))) 

Ergo: Phòng một người có một lối ra North vào phòng hai, phòng hai có một lối ra South trở lại phòng một.

Tuy nhiên, như bạn có thể tưởng tượng, điều này sai: firstRoom val không được xác định khi tạo, vì vậy hãy cố gắng tham chiếu nó trong quá trình gán sẽ không hoạt động.

Tôi chắc chắn điều này là đúng đối với hầu hết, nếu không phải tất cả ngôn ngữ lập trình. Câu hỏi của tôi: Làm cách nào để giải quyết điều này mà không cần làm cho đối tượng Room của tôi có thể thay đổi được? Tôi chỉ đơn giản có thể tạo một vài đối tượng Room và thêm các lối thoát cho chúng sau đó, nhưng điều đó làm cho khả năng tắt tiếng Room và là bài tập cá nhân tôi cố gắng tránh điều đó.

Trả lời

17

Non-Recursive

Tôi nghĩ rằng lựa chọn tốt nhất của bạn là để làm một cái gì đó như thế này

object Rooms { 
    case class Room(title: String) { 
    def exits = exitMap(this) 
    } 
    val first:Room = Room("first") 
    val second:Room = Room("second") 

    private val exitMap = Map(first -> Map("S" -> second), second -> Map("N" -> first)) 
} 

scala> Rooms.first.title 
res: String = first 

scala> Rooms.first.exits 
res: scala.collection.immutable.Map[java.lang.String,Rooms.Room] = Map(S -> Room(second)) 

Nó là hoàn toàn không thể thay đổi và bạn tránh recursions khó chịu.

Recursive

Xây dựng một cấu trúc đệ quy sẽ chăm sóc nhiều hơn nữa như Scala là không lười biếng theo mặc định. Cụ thể, không thể tạo tham số case class lười biếng hoặc gọi theo tên. Vì vậy, chúng ta sẽ phải sử dụng cấu trúc dữ liệu chuyên dụng cho việc này.

Một lựa chọn có thể được sử dụng Stream s:

case class LazyRoom(title: String, exits: Stream[LazyRoom]) 

object LazyRooms { 
    lazy val nullRoom: LazyRoom = LazyRoom("nullRoom", Stream.empty) 
    lazy val first: LazyRoom = LazyRoom("first", nullRoom #:: second #:: Stream.empty) 
    lazy val second: LazyRoom = LazyRoom("second", nullRoom #:: first #:: Stream.empty) 
} 

scala> LazyRooms.first.exits(1).title 
res> String: second 

Để được lưu bên, tôi có tiền tố phòng giả trước mỗi Stream để tránh truy cập sớm. (Một luồng chỉ lười ở đuôi nhưng không nằm trong đầu nó.) Một cấu trúc dữ liệu chuyên dụng có thể tránh được điều này.

sạch lên phiên bản

Chúng ta có thể làm tốt hơn với một hàm helper gọi bằng tên để làm công việc bẩn:

case class LazyRoom(title: String, exitMap: Stream[Map[String, LazyRoom]]) { 
    def exits = exitMap(1) // skip the Streams empty head 
} 

def _exitMap(mappedItems: => Map[String, LazyRoom]) = { 
    Map[String, LazyRoom]() #:: 
    mappedItems #:: 
    Stream.empty 
} 

object LazyRooms { 
    lazy val first: LazyRoom = LazyRoom("first", _exitMap(Map("South" -> second))) 
    lazy val second: LazyRoom = LazyRoom("second", _exitMap(Map("North" -> first))) 
} 

scala> LazyRooms.first.exits 
res: Map[String,LazyRoom] = Map(South -> LazyRoom(second,Stream(Map(), ?))) 
+0

Câu trả lời sử thi, bạn xứng đáng nhận được tất cả những cuộc nổi dậy mà bạn có thể nhận được. Không chỉ bạn cung cấp một câu trả lời đơn giản, mà còn là một câu trả lời sâu hơn về một tính năng ngôn ngữ mà tôi đã không có cơ hội sử dụng hoặc tìm thấy mục đích. Tôi đã gặp những dòng suối một hoặc hai lần khi tìm kiếm câu trả lời này, nhưng không phải là một đầu mối làm thế nào họ sẽ giải quyết vấn đề này. Nhiều nghĩa vụ. – fwielstra

0

Một giải pháp thay thế, mà không may (tôi nghĩ) doesn 'công việc t với trường hợp các lớp:

class Room(val title: String, _exits : => Map[String, Room]) { lazy val exits = _exits } 
val room1 : Room = new Room("A room", Map("N" -> room2)) 
val room2 : Room = new Room("Another room", Map("S" -> room1)) 

một lựa chọn khác là sử dụng một 'lười biếng' bản đồ:

case class Room(val title : String, exits : Map[String,() => Room]) 
val room1 : Room = Room("A room", Map("N" -> (() => room2))) 
val room2 : Room = Room("Another room", Map("S" -> (() => room1))) 

Cú pháp sẽ đẹp hơn nếu bạn thực hiện triển khai bản đồ lười biếng của riêng bạn mở rộng đặc điểm scala.collection.immutable.Map.

2

Tôi hoàn toàn đồng ý với câu trả lời của Debilski, nhưng tôi không thể cưỡng lại thay thế phương thức exists với giá trị val exists lười biếng, ngăn việc tra cứu xảy ra lặp đi lặp lại.

object Rooms { 
    case class Room(title: String) { 
    lazy val exits = exitMap(this) 
    } 
    val first:Room = Room("first") 
    val second:Room = Room("second") 

    private val exitMap = Map(first -> Map("S" -> second), second -> Map("N" -> first)) 
} 
Các vấn đề liên quan