Хотя Scala Option
будет работать здесь, как указывали два других ответа, более идиоматическим функциональным подходом было бы использование «ленивого списка» - или Stream
в Scala - представлять множество решений.
Я обнаружил, что пишу такой код, например:
trait Node[A] {
def children: Stream[A with Node[A]]
def dfs(f: A => Boolean): Stream[A] = this.children.flatMap {
child => if (f(child)) Stream(child) else child.dfs(f)
}
}
Теперь предположим, что у меня есть класс Board
, который расширяет Node[Board]
и который имеет реализацию метода children
, который возвращает все допустимые платы с одним дополнительным компонентом. Предположим, у него также есть некоторые другие полезные вещи, такие как size
метод, объект-компаньон с empty
и т. Д.
Затем я могу написать следующее, чтобы получить Stream
решений:
val solutions = Board.empty.dfs(_.size == 8)
A Stream
ленив и оценивает только его голову, поэтому сейчас мы только обыскали дерево достаточно далеко, чтобы найти первое решение. Мы можем получить это решение, используя head
:
scala> solutions.head
res1: Board =
o . . . . . . .
. . . . o . . .
. . . . . . . o
. . . . . o . .
. . o . . . . .
. . . . . . o .
. o . . . . . .
. . . o . . . .
Или что угодно. Но я могу также получить другие результаты, если я хочу их:
scala> solutions(10)
res2: Board =
. o . . . . . .
. . . . . . o .
. . . . o . . .
. . . . . . . o
o . . . . . . .
. . . o . . . .
. . . . . o . .
. . o . . . . .
При этом поиск по дереву будет достаточным, чтобы найти десятое решение, а затем останавливается.
Большим преимуществом Stream
перед подходом Option
является то, что я могу получить дополнительные результаты, если они мне нужны, без дополнительной оплаты за первый.