Skip to content

Instantly share code, notes, and snippets.

@mchericoni
Created February 27, 2015 11:33
Show Gist options
  • Save mchericoni/936c8ddc798606ca2427 to your computer and use it in GitHub Desktop.
Save mchericoni/936c8ddc798606ca2427 to your computer and use it in GitHub Desktop.
Breadth-first search problem in Scala
object SumProblem {
// States
type State = Int
val initialState: State = 5 //> initialState : SumProblem.State = 5
val target: State = 23 //> target : SumProblem.State = 23
// Moves
class Move(addend: Int) {
def change(state: State): State = state + addend
override def toString = addend.toString
}
// Legal Moves
val legalMoves: List[Move] = List(new Move(4), new Move(7), new Move(6), new Move(11))
//> legalMoves : List[SumProblem.Move] = List(4, 7, 6, 11)
// Example of applying a Move and obtaining a new State
legalMoves(0) change initialState //> res0: SumProblem.State = 9
// Paths
class Path(history: List[Move]) {
def extend(move: Move): Path = new Path(move :: history)
def endState: State = (history foldRight initialState)(_ change _)
override def toString = initialState + " + " + (history.reverse mkString (" + ")) + " = " + endState
}
// The initial Path
val initialPath = new Path(Nil) //> initialPath : SumProblem.Path = 5 + = 5
// Example of Path after 2 moves
new Path(List(legalMoves(0), legalMoves(2))) //> res1: SumProblem.Path = 5 + 6 + 4 = 15
// Where can we go from the initial state with one move?
legalMoves map initialPath.extend //> res2: List[SumProblem.Path] = List(5 + 4 = 9, 5 + 7 = 12, 5 + 6 = 11, 5 + 11
//| = 16)
// 2nd iteration
val more = for {
path <- legalMoves map initialPath.extend
next <- legalMoves map path.extend
} yield next //> more : List[SumProblem.Path] = List(5 + 4 + 4 = 13, 5 + 4 + 7 = 16, 5 + 4
//| + 6 = 15, 5 + 4 + 11 = 20, 5 + 7 + 4 = 16, 5 + 7 + 7 = 19, 5 + 7 + 6 = 18,
//| 5 + 7 + 11 = 23, 5 + 6 + 4 = 15, 5 + 6 + 7 = 18, 5 + 6 + 6 = 17, 5 + 6 + 11
//| = 22, 5 + 11 + 4 = 20, 5 + 11 + 7 = 23, 5 + 11 + 6 = 22, 5 + 11 + 11 = 27)
//|
// All the possible paths starting from a set of paths, given as a Stream
def from(paths: Set[Path]): Stream[Set[Path]] =
if (paths.isEmpty) Stream.Empty
else {
val more = for {
path <- paths
next <- legalMoves map path.extend
if (next.endState <= target)
} yield next
paths #:: from(more)
} //> from: (paths: Set[SumProblem.Path])Stream[Set[SumProblem.Path]]
// All the paths from the initial empty Path
val pathSets = from(Set(initialPath)) //> pathSets : Stream[Set[SumProblem.Path]] = Stream(Set(5 + = 5), ?)
// Print the first 3 elements of the Stream
pathSets take 3 foreach println //> Set(5 + = 5)
//| Set(5 + 4 = 9, 5 + 7 = 12, 5 + 6 = 11, 5 + 11 = 16)
//| Set(5 + 6 + 7 = 18, 5 + 6 + 6 = 17, 5 + 6 + 11 = 22, 5 + 4 + 7 = 16, 5 + 11
//| + 7 = 23, 5 + 11 + 6 = 22, 5 + 4 + 6 = 15, 5 + 7 + 11 = 23, 5 + 6 + 4 = 15
//| , 5 + 7 + 6 = 18, 5 + 11 + 4 = 20, 5 + 4 + 11 = 20, 5 + 4 + 4 = 13, 5 + 7 +
//| 4 = 16, 5 + 7 + 7 = 19)
// Find the solutions (if any)
for {
pathSet <- pathSets
path <- pathSet
if path.endState == target
} yield path //> res3: scala.collection.immutable.Stream[SumProblem.Path] = Stream(5 + 11 +
//| 7 = 23, ?)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment