Я бы пошел с рекурсивной версией, но вот правильная реализация Stream
версии:
var board: TicTacToeBoard = new TicTacToeBoard
def start() {
def initialBoard: TicTacToeBoard = new TicTacToeBoard
def initialGameState: GameState = new XMovesNext
def gameIterator = Stream.iterate(initialBoard -> initialGameState) _
def game: Stream[GameState] = {
val (moves, end) = gameIterator {
case (board, gameState) =>
val position: Int = getSelectionFromUser
val updatedBoard = board.updated(position, gameState.nextTurn)
(updatedBoard, getGameState(board))
}.span { case (_, gameState) => !gameState.isGameFinished }
(moves ::: end.take(1)) map { case (_, gameState) => gameState }
}
game foreach outputState
}
Это выглядит страннее, чемдолжно.В идеале я бы использовал takeWhile
, а затем map
, но это не сработает, так как случай last будет опущен!
Если ходы игры можно отбросить, тогда будет работать dropWhile
, за которым следует head
.Если бы у меня был побочный эффект (outputState
) вместо Stream
, я мог бы пойти по этому пути, но иметь побочный эффект внутри Stream
намного хуже, чем var
с петлей while
.
Итак, вместо этого я использую span
, что дает мне и takeWhile
, и dropWhile
, но вынуждает меня сохранять промежуточные результаты - что может быть очень плохо, если память вызывает беспокойство, поскольку вся игра будетхраниться в памяти, потому что moves
указывает на голову Stream
.Поэтому мне пришлось заключить все это в другой метод, game
.Таким образом, когда я foreach
через результаты game
, ничего не будет указывать на Stream
head
.
Другой альтернативой будет избавление от другие побочные эффекты у вас есть: getSelectionFromUser
.Вы можете избавиться от этого с помощью Iteratee
, а затем можете сохранить последний ход и повторно применить его.
ИЛИ ... вы можете написать себе метод takeTo
ииспользуйте это.