fwi*_*tra 21 scala immutability
我在周日下午摆弄着,正试图创造一种"房间"结构.基本上,一个Room
对象有许多出口,每个出口都指向其他Room
的出口.现在,我要创建的第一件事是两个Room
相互连接,最好是在一个赋值语句中.像这样:
case class Room(title: String, exits: Map[Direction.Direction, Room])
val firstRoom = Room("A room", Map(North -> Room("Another room", Map(South -> firstRoom))))
Run Code Online (Sandbox Code Playgroud)
Ergo:第一个房间有一个出口North
到第二个房间,第二个房间有一个出口South
回到第一个房间.
但是,正如您可以想象的那样,这是错误的:firstRoom
在创建val时未定义val,因此在分配期间尝试引用它将不起作用.
对于大多数(如果不是全部)编程语言,我很确定这是正确的.我的问题:如何在不使我的Room
对象变为可变的情况下解决这个问题?我可以简单地创建一些Room
对象并在之后添加出口,但这使得Room
s 变得可变,并且作为个人练习我试图避免这种情况.
Deb*_*ski 17
非递归
我认为你最好的选择是做这样的事情
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))
Run Code Online (Sandbox Code Playgroud)
它是完全不可变的,你可以避免令人讨厌的递归.
递归
构造递归结构需要更加小心,因为默认情况下Scala不是懒惰的.具体而言,无法创建惰性或按名称调用case class
参数.因此,我们将不得不求助于专门的数据结构.
一种选择可能是使用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
Run Code Online (Sandbox Code Playgroud)
为了保存一面,我在每个房间前面都有一个假房间,Stream
以避免过早访问.(Stream的尾部只是懒惰但不是它的头部.)专用数据结构可能能够避免这种情况.
清理版本
我们可以通过调用名称辅助函数来做更好的工作:
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(), ?)))
Run Code Online (Sandbox Code Playgroud)