我是一個Scala n00b(但我有與其他語言的經驗),我正在學習語言,因爲我發現時間 - 非常享受它迄今!評論我的Scala代碼
通常當學習一種新的語言時,我所做的第一件事是實現Conway's Game of Life,因爲它足夠複雜,足以讓人對語言有一個很好的理解,但是它的範圍足夠小以便能夠在幾個小時內wh起其中大部分花費在語法上)。
Anyhoo,經歷了Scala的這個練習,我希望Scala大師可能會看看我已經結束的代碼並提供反饋。我追求任何東西 - 算法改進(特別是併發解決方案!),文體改進,替代API或語言構造,對我的函數名稱的長度感到厭惡 - 無論您有什麼反饋,我都渴望聽到它!
您應該可以通過scala GameOfLife.scala
運行以下腳本 - 默認情況下它將運行帶有單個滑翔機的20x20板 - 請隨時嘗試。
// CONWAY'S GAME OF LIFE (SCALA)
abstract class GameOfLifeBoard(val aliveCells : Set[Tuple2[Int, Int]])
{
// Executes a "time tick" - returns a new board containing the next generation
def tick : GameOfLifeBoard
// Is the board empty?
def empty : Boolean = aliveCells.size == 0
// Is the given cell alive?
protected def alive(cell : Tuple2[Int, Int]) : Boolean = aliveCells contains cell
// Is the given cell dead?
protected def dead(cell : Tuple2[Int, Int]) : Boolean = !alive(cell)
}
class InfiniteGameOfLifeBoard(aliveCells : Set[Tuple2[Int, Int]])
extends GameOfLifeBoard(aliveCells)
{
// Executes a "time tick" - returns a new board containing the next generation
override def tick : GameOfLifeBoard = new InfiniteGameOfLifeBoard(nextGeneration)
// The next generation of this board
protected def nextGeneration : Set[Tuple2[Int, Int]] = aliveCells flatMap neighbours filter shouldCellLiveInNextGeneration
// Should the given cell should live in the next generation?
protected def shouldCellLiveInNextGeneration(cell : Tuple2[Int, Int]) : Boolean = (alive(cell) && (numberOfAliveNeighbours(cell) == 2 || numberOfAliveNeighbours(cell) == 3)) ||
(dead(cell) && numberOfAliveNeighbours(cell) == 3)
// The number of alive neighbours for the given cell
protected def numberOfAliveNeighbours(cell : Tuple2[Int, Int]) : Int = aliveNeighbours(cell) size
// Returns the alive neighbours for the given cell
protected def aliveNeighbours(cell : Tuple2[Int, Int]) : Set[Tuple2[Int, Int]] = aliveCells intersect neighbours(cell)
// Returns the coordinates of all of the neighbouring cells of the given cell
protected def neighbours(cell : Tuple2[Int, Int]) : Set[Tuple2[Int, Int]] = Set((cell._1-1, cell._2-1), (cell._1, cell._2-1), (cell._1+1, cell._2-1),
(cell._1-1, cell._2), (cell._1+1, cell._2),
(cell._1-1, cell._2+1), (cell._1, cell._2+1), (cell._1+1, cell._2+1))
// Information on where the currently live cells are
protected def xVals = aliveCells map { cell => cell._1 }
protected def xMin = (xVals reduceLeft (_ min _)) - 1
protected def xMax = (xVals reduceLeft (_ max _)) + 1
protected def xRange = xMin until xMax + 1
protected def yVals = aliveCells map { cell => cell._2 }
protected def yMin = (yVals reduceLeft (_ min _)) - 1
protected def yMax = (yVals reduceLeft (_ max _)) + 1
protected def yRange = yMin until yMax + 1
// Returns a simple graphical representation of this board
override def toString : String =
{
var result = ""
for (y <- yRange)
{
for (x <- xRange)
{
if (alive (x,y)) result += "# "
else result += ". "
}
result += "\n"
}
result
}
// Equality stuff
override def equals(other : Any) : Boolean =
{
other match
{
case that : InfiniteGameOfLifeBoard => (that canEqual this) &&
that.aliveCells == this.aliveCells
case _ => false
}
}
def canEqual(other : Any) : Boolean = other.isInstanceOf[InfiniteGameOfLifeBoard]
override def hashCode = aliveCells.hashCode
}
class FiniteGameOfLifeBoard(val boardWidth : Int, val boardHeight : Int, aliveCells : Set[Tuple2[Int, Int]])
extends InfiniteGameOfLifeBoard(aliveCells)
{
override def tick : GameOfLifeBoard = new FiniteGameOfLifeBoard(boardWidth, boardHeight, nextGeneration)
// Returns the coordinates of all of the neighbouring cells of the given cell
override protected def neighbours(cell : Tuple2[Int, Int]) : Set[Tuple2[Int, Int]] = super.neighbours(cell) filter { cell => cell._1 >= 0 && cell._1 < boardWidth &&
cell._2 >= 0 && cell._2 < boardHeight }
// Information on where the currently live cells are
override protected def xRange = 0 until boardWidth
override protected def yRange = 0 until boardHeight
// Equality stuff
override def equals(other : Any) : Boolean =
{
other match
{
case that : FiniteGameOfLifeBoard => (that canEqual this) &&
that.boardWidth == this.boardWidth &&
that.boardHeight == this.boardHeight &&
that.aliveCells == this.aliveCells
case _ => false
}
}
override def canEqual(other : Any) : Boolean = other.isInstanceOf[FiniteGameOfLifeBoard]
override def hashCode : Int =
{
41 * (
41 * (
41 + super.hashCode
) + boardHeight.hashCode
) + boardWidth.hashCode
}
}
class GameOfLife(initialBoard: GameOfLifeBoard)
{
// Run the game of life until the board is empty or the exact same board is seen twice
// Important note: this method does NOT necessarily terminate!!
def go : Unit =
{
var currentBoard = initialBoard
var previousBoards = List[GameOfLifeBoard]()
while (!currentBoard.empty && !(previousBoards contains currentBoard))
{
print(27.toChar + "[2J") // ANSI: clear screen
print(27.toChar + "[;H") // ANSI: move cursor to top left corner of screen
println(currentBoard.toString)
Thread.sleep(75)
// Warning: unbounded list concatenation can result in OutOfMemoryExceptions ####TODO: replace with LRU bounded list
previousBoards = List(currentBoard) ::: previousBoards
currentBoard = currentBoard tick
}
// Print the final board
print(27.toChar + "[2J") // ANSI: clear screen
print(27.toChar + "[;H") // ANSI: move cursor to top left corner of screen
println(currentBoard.toString)
}
}
// Script starts here
val simple = Set((1,1))
val square = Set((4,4), (4,5), (5,4), (5,5))
val glider = Set((2,1), (3,2), (1,3), (2,3), (3,3))
val initialBoard = glider
(new GameOfLife(new FiniteGameOfLifeBoard(20, 20, initialBoard))).go
//(new GameOfLife(new InfiniteGameOfLifeBoard(initialBoard))).go
// COPYRIGHT PETER MONKS 2010
一個具體問題:我刪除了所有的函數的返回類型在一個點(Scala的類型推斷FTW!),卻發現代碼實際上得難以閱讀。有沒有關於何時離開返回類型的任何約定,讓Scala找出它們(除了那些必要的情況之外)?
放在同一行左括號。如果你用Java編碼,那麼在那裏也是一樣。 – OscarRyz 2010-06-17 23:10:37
謝謝,但我是一個老傻瓜ANSI-C風格的傢伙,不太可能很快改變我的括號/縮進樣式。 ;-) – Peter 2010-06-17 23:16:51
這就是我想的:)但是,你說:*批評我的代碼* :)我認爲這就像學習一門自然語言,你將永遠有一個外國口音。 – OscarRyz 2010-06-17 23:43:36