N-Queens Combinatorial Problem - Polyglot FP for fun and profit - Haskell and Scala - Part 3

Philip Schwarz
Philip SchwarzSoftware development is what I am passionate about
N-Queens Combinatorial Problem
Learn how to write FP code that displays a graphical representation of all the numerous N-Queens solutions for N=4,5,6,7,8
See how to neatly solve the problem by exploiting its self-similarity and using a divide and conquer approach
Make light work of assembling multiple images into a whole, by exploiting Doodle’s facilities for combining images using a relative layout
See relevant FP functions, like Foldable’s intercalate and intersperse, in action
@philip_schwarz
slides by https://www.slideshare.net/pjschwarz
Part 3
Doodle
Polyglot FP for Fun and Profit – Haskell and Scala
Welcome to Part 3 of this series. In this part, we are going to write a new
program that displays, all together, the results of queens(N) for N = 4, 5, 6, 7, 8.
The next slide shows both the program (from Part 2) that displays the board for
a single solution, and the beginnings of the new program, which will reuse
some logic from both Part 1 and Part 2.
def display(ns: List[Int])(image: Image): Unit =
val frameTitle = "N-Queens Problem - Solutions for N = ${ns.mkString(",")}"
val frameWidth = 1800
val frameHeight = 1000
val frameBackgroundColour = Color.white
val frame =
Frame.size(frameWidth,frameHeight)
.title(frameTitle)
.background(frameBackgroundColour)
image.draw(frame)
@main def main =
val ns = List(4,5,6,7,8)
ns map queens pipe makeResultsImage pipe display(ns)
val makeResultsImage: List[List[List[Int]]] => Image = ??? // to be implemented
def showQueens(solution: List[Int]): Int =
val n = solution.length
val frameTitle = s"{n}-Queens Problem – A solution"
val frameWidth = 1000
val frameHeight = 1000
val frameBackgroundColour = Color.white
val frame =
Frame.size(frameWidth,frameHeight)
.title(frameTitle)
.background(frameBackgroundColour)
show(solution).draw(frame)
def show(queens: List[Int]): Image =
val square = Image.square(100).strokeColor(Color.black)
val emptySquare: Image = square.fillColor(Color.white)
val fullSquare: Image = square.fillColor(Color.orangeRed)
val squareImageGrid: List[List[Image]] =
for col <- queens.reverse
yield List.fill(queens.length)(emptySquare)
.updated(col,fullSquare)
combine(squareImageGrid)
val beside = Monoid.instance[Image](Image.empty, _ beside _)
val above = Monoid.instance[Image](Image.empty, _ above _)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll beside)(above)
@main def main =
val solution = List(3,1,6,2,5,7,4,0)
showQueens(solution)
def onDiagonal(row: Int, column: Int, otherRow: Int, otherColumn: Int) =
math.abs(row - otherRow) == math.abs(column - otherColumn)
def safe(queen: Int, queens: List[Int]): Boolean =
val (row, column) = (queens.length, queen)
val safe: ((Int,Int)) => Boolean = (nextRow, nextColumn) =>
column != nextColumn && !onDiagonal(column, row, nextColumn, nextRow)
zipWithRows(queens) forall safe
def zipWithRows(queens: List[Int]): Iterable[(Int,Int)] =
val rowCount = queens.length
val rowNumbers = rowCount - 1 to 0 by -1
rowNumbers zip queens
def queens(n: Int): List[List[Int]] =
def placeQueens(k: Int): List[List[Int]] =
if k == 0
then List(List())
else
for
queens <- placeQueens(k - 1)
queen <- 1 to n
if safe(queen, queens)
yield queen :: queens
placeQueens(n)
We are switching from the program
on the left, to the one on the right.
New code is on a green background.
In the new program, generating an image is the responsibility of makeResultsImage.
See next slide for an
explanation of pipe.
The program on the left is from Part 2. It displays the board
for a single solution. The program on the right uses the queens
function (and ancillary functions) from Part 1. It displays, all
together, the results of queens(N) for N = 4, 5, 6, 7, 8.
Scala’s pipe function allows us to take an expression consisting of a number of nested function
invocations, e.g. f(g(h(x)), and turn it into an equivalent expression in which the functions appear
in the order in which they are invoked, i.e. h, g and f, rather than in the inverse order, i.e. f, g and h.
assert(square(twice(inc(3))) == 64)
assert ((3 pipe inc pipe twice pipe square) == 64)
def inc(n: Int): Int = n + 1
def twice(n: Int): Int = n * 2
def square(n: Int): Int = n * n
@philip_schwarz
Here is one example
@main def main =
val ns = List(4,5,6,7,8)
display(ns)(makeResultsImage(ns map queens))
@main def main =
val ns = List(4,5,6,7,8)
ns map queens pipe makeResultsImage pipe display(ns)
We are using pipe to make our main function easier to understand
@main def main =
val ns = List(4,5,6,7,8)
ns map queens pipe makeResultsImage pipe display(ns)
val makeResultsImage: List[List[List[Int]]] => Image = ???
type Solution = List[Int]
val makeResultsImage: List[List[Solution]] => Image = ???
val makeResultsImage: List[Solutions] => Image = ???
type Solutions = List[Solution]
Our current objective is to
implement the makeResultsImage
function invoked by main.
Let’s begin by introducing a couple of type aliases to aid comprehension.
If at some point, while reading the next three slides, you feel a strong sense of déjà vu, that is to be expected.
There is a lot of symmetry between the slides, and the code that they contain.
That’s because the problem that we are working on exhibits a good degree of self-similarity.
The problem looks very similar at three different levels:
• When we operate at the single Solution level, we need to create an image of a grid of squares (a solution board).
• When we operate at the multiple Solution level, we need to create an image of a grid of boards (the solution boards for some N).
• When we operate at the multiple Solutions level, we need to create an image of a grid of grids of boards (i.e. all the solution boards for
N=4,5,6,7,8).
If things appear to get a bit confusing at times, keep a cool head by focusing on the function signatures at play, and by reminding yourself
that all we are doing is divide and conquer.
type Solution = List[Int]
type Solutions = List[Solution]
val makeResultsImage: List[Solutions] => Image = makeSolutionsImageGrid andThen combineWithPadding
type Grid[A] = List[List[A]]
type Solution = List[Int]
type Solutions = List[Solution]
makeSolutionsImageGrid : List[Solutions] => Grid[Image] combineWithPadding : Grid[Image] => Image
To turn multiple Solutions elements into an image, we are going to first create an Image for
each Solutions element, then arrange the resulting images in a Grid, and finally combine the
images into a single compound image, adding padding around images as we combine them.
Creating the images, and arranging them into a grid, will be done by makeSolutionsImageGrid,
whereas combining the images into a single compound image, inserting padding around the
images, will be done by combineWithPadding.
In order to create a Grid[Image], makeSolutionsImageGrid must create an image for each
Solutions element.
To help with that, on the next slide we define a function called makeSolutionsImage, which
given a Solutions element, returns an Image.
This is analogous to makeResultsImage, but operates on an individual Solutions element
rather than on a list of such elements, so it operates one level below makeResultsImage.
val makeSolutionsImage: List[Solution] => Image = makeBoardImageGrid andThen combineWithPadding
To turn multiple Solution elements into an image, we are going to first create an Image for
each Solution, then arrange the images in a Grid, and finally combine the images into a single
compound image, adding padding around images as we combine them.
Creating the images, and arranging them into a grid, will be done by makeBoardImageGrid,
whereas combining the images into a single compound image, inserting padding around the
images, will be done by combineWithPadding (yes, we introduced it on the previous slide).
makeBoardImageGrid : List[Solution] => Grid[Image] combineWithPadding : Grid[Image] => Image
In order to create a Grid[Image], makeBoardImageGrid must create an image for each
Solution element.
To help with that, on the next slide we define a function called makeBoardImage, which
given a Solution element, returns an Image.
This is analogous to makeSolutionsImage, but operates on an individual Solution element
rather than on a list of such elements, so it operates one level below makeSolutionsImage.
type Grid[A] = List[List[A]]
type Solution = List[Int]
type Solutions = List[Solution]
val makeBoardImage: Solution => Image = makeSquareImageGrid andThen combine
To turn a Solution, which represents a chess board, into an image, we are going to
first create an Image for each square in the Solution, then arrange the images in a
Grid, and finally combine the images into a single compound image.
Creating the images, and arranging them into a grid, will be done by
makeSquareImageGrid, whereas combining the images into a single compound
image will be done by combine (yes, we implemented such a function in Part 2).
makeSquareImageGrid : Solution => Grid[Image] combine : Grid[Image] => Image
type Grid[A] = List[List[A]]
type Solution = List[Int]
type Solutions = List[Solution]
The next slide visualises the self-similarity of the problem we are
working on, and its amenability to a divide and conquer approach.
@philip_schwarz
a cell in a board a single solution for N = n
a board (solution)
all solutions for N = n
all solutions for N = n
all solutions for N = n, n+1, n+2, n+3, n+4
val makeResultsImage: List[Solutions] => Image =
makeSolutionsImageGrid andThen combineWithPadding
makeSolutionsImageGrid: List[Solutions] => Grid[Image]
combineWithPadding: Grid[Image] => Image
val makeSolutionsImage: List[Solution] => Image =
makeBoardImageGrid andThen combineWithPadding
makeBoardImageGrid: List[Solution] => Grid[Image]
val makeBoardImage: Solution => Image =
makeSquareImageGrid andThen combine
makeSquareImageGrid: Solution => Grid[Image]
combine: Grid[Image] => Image
Combine a grid of images into a composite image with padding around the images
Create an image of a grid of squares (a solution board)
Create an image of a grid of boards (the solution boards for some N)
Create an image of a grid of grids of boards (i.e. all the solution boards for N=4,5,6,7,8)
Create a grid of square images (a solution board)
Create a grid of board images (the solution boards for some N)
Create a grid of images of grids of boards (i.e. all the solution boards for N=4,5,6,7,8)
Combine a grid of images into a composite image with no padding around the images
Here are the functions that we have identified so far.
We already have implementations for the first three functions.
On the next slide, we start implementing the next three functions, which create grids of images.
type Grid[A] = List[List[A]]
type Solution = List[Int]
type Solutions = List[Solution]
makeSolutionsImageGrid: List[Solutions] => Grid[Image]
makeBoardImageGrid: List[Solution] => Grid[Image]
makeSquareImageGrid: Solution => Grid[Image]
Since all three of these functions have to create a grid, they will
have some logic in common.
Let’s put that shared logic in a function called makeImageGrid.
def makeImageGrid[A](as: List[A], makeImage: A => Image, gridWidth: Int): Grid[Image] =
as map makeImage grouped gridWidth toList
Implementing makeSolutionsImageGrid and
makeBoardImageGrid is now simply a matter of
invoking makeImageGrid.
def makeSolutionsImageGrid(queensResults: List[Solutions]): Grid[Image] =
makeImageGrid(queensResults, makeSolutionsImage, gridWidth = 1)
def makeBoardImageGrid(solutions: List[Solution]): Grid[Image] =
makeImageGrid(solutions, makeBoardImage, gridWidth = 17)
See the previous slide for implementations of
makeSolutionsImage and makeBoardImage.
We are creating a degenerate grid, one with
just 1 column. We are doing so simply because
it happens to result in an effective layout.
Again, it just happens that creating a grid with 17
columns results in a better layout of solution boards than
is otherwise the case.
type Grid[A] = List[List[A]]
type Solution = List[Int]
type Solutions = List[Solution]
While Implementing makeSolutionsImageGrid and makeBoardImageGrid was simply a
matter of invoking makeImageGrid, implementing makeSquareImageGrid is more involved.
def makeSquareImageGrid(columnIndices:Solution): Grid[Image] =
val n = columnIndices.length
val (emptySquare, fullSquare) = makeSquareImages(n)
val occupiedCells: List[Boolean] =
columnIndices.reverse flatMap { col => List.fill(n)(false).updated(col-1,true) }
val makeSquareImage: Boolean => Image = if (_) fullSquare else emptySquare
makeImageGrid(occupiedCells, makeSquareImage, gridWidth = n)
def makeSquareImages(n: Int): (Image,Image) =
val emptySquareColour = n match
case 4 => Color.limeGreen
case 5 => Color.lime
case 6 => Color.springGreen
case 7 => Color.paleGreen
case 8 => Color.greenYellow
case other => Color.white
val square: Image = Image.square(10).strokeColor(Color.black)
val emptySquare: Image = square.fillColor(emptySquareColour)
val fullSquare: Image = square.fillColor(Color.orangeRed)
(emptySquare, fullSquare)
To help distinguish the solution boards for different values of N (4,5,6,7,8), we are giving
their empty squares of a different shade of green.
val makeResultsImage: List[Solutions] => Image =
makeSolutionsImageGrid andThen combineWithPadding
combineWithPadding: Grid[Image] => Image
val makeSolutionsImage: List[Solution] => Image =
makeBoardImageGrid andThen combineWithPadding
val makeBoardImage: Solution => Image =
makeSquareImageGrid andThen combine
combine: Grid[Image] => Image
Looking back at the implementations of our three functions
for creating images, now that we have implemented the
functions that create image grids, it is time to implement
combine and combineWithPadding.
On the next slide we start implementing combineWithPadding.
@philip_schwarz
import cats.Monoid
val beside = Monoid.instance[Image](Image.empty, _ beside _)
val above = Monoid.instance[Image](Image.empty, _ above _)
def combine(imageGrid: List[List[Image]]): Image =
import cats.implicits._
imageGrid.foldMap(_ combineAll beside)(above)
Remember the implementation of the
combine function that we used, in Part 2, to
take a grid of images and produce a
compound image that is their composition?
We need to implement combineWithPadding, a function that differs from combine in that instead of just combining the images
contained in its image grid parameter, it also needs to insert a padding image between neighbouring images as it does that.
combineWithPadding: Grid[Image] => Image
The combine function first folds the images in a row (combineAll is just an alias for fold) using the beside monoid, and then
folds the resulting row images using the above monoid. The combineWithPadding function needs to fold images in the same
way, but in addition, it also needs to insert a padding image between each pair of such images.
This is clearly a job for the intercalate function provided by Cats’ Foldable type class!
def combineWithPadding(images: Grid[Image]): Image =
combineWithPadding(images, paddingImage)
def combineWithPadding(images: Grid[Image], paddingImage: Image): Image =
import cats.implicits._
images.map(row => row.intercalate(paddingImage)(beside))
.intercalate(paddingImage)(above)
val paddingImage = Image.square(10).strokeColor(Color.white).fillColor(Color.white)
Let’s go ahead and implement combineWithPadding using the
intercalate function provided by Cats’ Foldable type class!
The padding consists of a white square with a width of 10 pixels.
def combine(images: Grid[Image]): Image =
combineWithPadding(images, paddingImage = Image.empty)
def combineWithPadding(images: Grid[Image], paddingImage: Image): Image =
import cats.Foldable
Foldable[List].intercalate (
images.map(row => Foldable[List].intercalate(row, paddingImage)(beside)),
paddingImage
)(above)
In case you find it useful, here is how the second combineWithPadding
function looks like if we use Foldable[List]explicitly.
def combine(imageGrid: List[List[Image]]): Image =
import cats.implicits._
imageGrid.foldMap(_ combineAll beside)(above)
Here again, for reference, is how we
implemented combine in Part 2.
What about the combine function, which
doesn’t do any padding? Why is it that a couple
of slides ago we said that we need to implement
it? We have already implemented it in Part 2
(see above).
While we can certainly just use the above
combine function, it is interesting to see how
much simpler the implementation becomes if we
leverage the combineWithPadding function that
we have just introduced.
No padding, is just padding
with an empty image. A bit silly
maybe, but attractively simple.
The next slide shows all of the code needed to display the N-Queens solutions for N=4,5,6,7,8.
While the queens function is included on the slide, this is purely to remind us of how the
solutions are produced, and so its three subordinate functions are not shown.
@philip_schwarz
def combine(images: Grid[Image]): Image =
combineWithPadding(images, paddingImage = Image.empty)
def combineWithPadding(images: Grid[Image]): Image =
combineWithPadding(images, paddingImage)
def combineWithPadding(images: Grid[Image], paddingImage: Image): Image =
import cats.implicits._
images.map(row => row.intercalate(paddingImage)(beside))
.intercalate(paddingImage)(above)
import cats.Monoid
val beside = Monoid.instance[Image](Image.empty, _ beside _)
val above = Monoid.instance[Image](Image.empty, _ above _)
val paddingImage = Image.square(10)
.strokeColor(Color.white)
.fillColor(Color.white)
val makeResultsImage: List[Solutions] => Image =
makeSolutionsImageGrid andThen combineWithPadding
val makeSolutionsImage: List[Solution] => Image =
makeBoardImageGrid andThen combineWithPadding
val makeBoardImage: Solution => Image =
makeSquareImageGrid andThen combine
def makeSquareImageGrid(columnIndices:Solution): Grid[Image] =
val n = columnIndices.length
val (emptySquare, fullSquare) = makeSquareImages(n)
val occupiedCells: List[Boolean] = columnIndices.reverse flatMap { col =>
List.fill(n)(false).updated(col-1,true) }
val makeSquareImage: Boolean => Image = if (_) fullSquare else emptySquare
makeImageGrid(occupiedCells, makeSquareImage, gridWidth = n)
def makeSquareImages(n: Int): (Image,Image) =
val emptySquareColour = n match
case 4 => Color.limeGreen
case 5 => Color.lime
case 6 => Color.springGreen
case 7 => Color.paleGreen
case 8 => Color.greenYellow
case other => Color.white
val square: Image = Image.square(10).strokeColor(Color.black)
val emptySquare: Image = square.fillColor(emptySquareColour)
val fullSquare: Image = square.fillColor(Color.orangeRed)
(emptySquare, fullSquare)
def makeImageGrid[A](as: List[A], makeImage: A => Image, gridWidth: Int): Grid[Image] =
as map makeImage grouped gridWidth toList
def makeSolutionsImageGrid(queensResults: List[Solutions]): Grid[Image] =
makeImageGrid(queensResults, makeSolutionsImage, gridWidth = 1)
def makeBoardImageGrid(solutions: List[Solution]): Grid[Image] =
makeImageGrid(solutions, makeBoardImage, gridWidth = 17)
type Grid[A] = List[List[A]]
type Solution = List[Int]
type Solutions = List[Solution]
@main def main =
val ns = List(4,5,6,7,8)
ns map queens pipe makeResultsImage pipe display(ns)
def display(ns: List[Int])(image: Image): Unit =
val frameTitle = "N-Queens Problem - Solutions for N = ${ns.mkString(",")}"
val frameWidth = 1800
val frameHeight = 1000
val frameBackgroundColour = Color.white
val frame = Frame.size(frameWidth,frameHeight)
.title(frameTitle)
.background(frameBackgroundColour)
image.draw(frame)
def queens(n: Int): List[List[Int]] =
def placeQueens(k: Int): List[List[Int]] =
if k == 0 then List(List())
else
for
queens <- placeQueens(k - 1)
queen <- 1 to n
if safe(queen, queens)
yield queen :: queens
placeQueens(n)
While the code on the previous slide works, I think the combineWithPadding function is doing too much. It is unnecessarily conflating two
responsibilities: inserting padding between images, and combining the images.
Let’s delete the combine and combineWithPadding functions on the left, and reinstate the earlier combine function, on the right.
def combine(images: Grid[Image]): Image =
combineWithPadding(images, paddingImage = Image.empty)
def combineWithPadding(images: Grid[Image]): Image =
combineWithPadding(images, paddingImage)
def combineWithPadding(images: Grid[Image], paddingImage: Image): Image =
import cats.implicits._
images.map(row => row.intercalate(paddingImage)(beside))
.intercalate(paddingImage)(above)
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll beside)(above)
val makeResultsImage: List[Solutions] => Image =
makeSolutionsImageGrid andThen combineWithPadding
val makeSolutionsImage: List[Solution] => Image =
makeBoardImageGrid andThen combineWithPadding
val makeResultsImage: List[Solutions] => Image =
makeSolutionsImageGrid andThen combine
val makeSolutionsImage: List[Solution] => Image =
makeBoardImageGrid andThen combine
We must now stop makeResultsImage and makeSolutionsImage
from using the combineWithPadding function that we have deleted.
Now that padding no longer gets introduced when images are
combined, when is it going to be introduced? See the next slide.
import scala.collection.decorators._
def insertPadding(images: Grid[Image]): Grid[Image] =
images map (_ intersperse paddingImage) intersperse List(paddingImage)
Let’s define a function called insertPadding, that takes a grid of images, and inserts a padding image between each pair of neighbouring
images. We can implement the function using the intersperse function provided by https://github.com/scala/scala-collection-contrib.
Except that when I try to use the intersperse function with Scala 3, I get a compilation error, so I
have opened an issue: https://github.com/scala/scala-collection-contrib/pull/146.
def insertPadding(images: Grid[Image]): Grid[Image] =
import scalaz._, Scalaz._
images map (_ intersperse paddingImage) intersperse List(paddingImage)
Luckily, the very same intersperse
function is also available in Scalaz.
def makePaddedImageGrid[A](as: List[A], makeImage: A => Image, gridWidth: Int): Grid[Image] =
makeImageGrid(as, makeImage, gridWidth) pipe insertPadding
def makeImageGrid[A](as: List[A], makeImage: A => Image, gridWidth: Int): Grid[Image] =
as map makeImage grouped gridWidth toList
def makeSolutionsImageGrid(queensResults: List[Solutions]): Grid[Image] =
makeImageGrid(queensResults, makeSolutionsImage, gridWidth = 1)
def makeBoardImageGrid(solutions: List[Solution]): Grid[Image] =
makeImageGrid(solutions, makeBoardImage, gridWidth = 17)
Now, remember makeImageGrid, the function
that we use to turn a list into a grid of images?
Now that we have defined insertPadding, we can use it to define a variant of makeImageGrid which, in
addition to creating a grid of images, inserts padding between those images.
Armed with the above function, we can now remedy the fact that we have eliminated the
combineWithPadding function. The padding that was previously inserted by using combineWithPadding,
will now be inserted by invoking makePaddedImageGrid rather than makeImageGrid. We need to make
the switch in the following two functions:
The next slide applies the additions/changes described on this slide, to
the code needed to display the N-Queens solutions for N=4,5,6,7,8.
List(1, 2, 3) intersperse 0 assert_=== List(1,0,2,0,3)
List(1, 2) intersperse 0 assert_=== List(1,0,2)
List(1) intersperse 0 assert_=== List(1)
nil[Int] intersperse 0 assert_=== nil[Int]
def combine(imageGrid: List[List[Image]]): Image =
imageGrid.foldMap(_ combineAll beside)(above)
import cats.Monoid
val beside = Monoid.instance[Image](Image.empty, _ beside _)
val above = Monoid.instance[Image](Image.empty, _ above _)
val paddingImage = Image.square(10)
.strokeColor(Color.white)
.fillColor(Color.white)
val makeResultsImage: List[Solutions] => Image =
makeSolutionsImageGrid andThen combineWithPadding
val makeSolutionsImage: List[Solution] => Image =
makeBoardImageGrid andThen combineWithPadding
val makeBoardImage: Solution => Image =
makeSquareImageGrid andThen combine
def makeSquareImageGrid(columnIndices:Solution): Grid[Image] =
val n = columnIndices.length
val (emptySquare, fullSquare) = makeSquareImages(n)
val occupiedCells: List[Boolean] = columnIndices.reverse flatMap { col =>
List.fill(n)(false).updated(col-1,true) }
val makeSquareImage: Boolean => Image = if (_) fullSquare else emptySquare
makeImageGrid(occupiedCells, makeSquareImage, gridWidth = n)
def makeSquareImages(n: Int): (Image,Image) =
val emptySquareColour = n match
case 4 => Color.limeGreen
case 5 => Color.lime
case 6 => Color.springGreen
case 7 => Color.paleGreen
case 8 => Color.greenYellow
case other => Color.white
val square: Image = Image.square(10).strokeColor(Color.black)
val emptySquare: Image = square.fillColor(emptySquareColour)
val fullSquare: Image = square.fillColor(Color.orangeRed)
(emptySquare, fullSquare)
def makeImageGrid[A](as: List[A], makeImage: A => Image, gridWidth: Int): Grid[Image] =
as map makeImage grouped gridWidth toList
def makeSolutionsImageGrid(queensResults: List[Solutions]): Grid[Image] =
makePaddedImageGrid(queensResults, makeSolutionsImage, gridWidth = 1)
def makeBoardImageGrid(solutions: List[Solution]): Grid[Image] =
makePaddedImageGrid(solutions, makeBoardImage, gridWidth = 17)
type Grid[A] = List[List[A]]
type Solution = List[Int]
type Solutions = List[Solution]
@main def main =
val ns = List(4,5,6,7,8)
ns map queens pipe makeResultsImage pipe display(ns)
def display(ns: List[Int])(image: Image): Unit =
val frameTitle = "N-Queens Problem - Solutions for N = ${ns.mkString(",")}"
val frameWidth = 1800
val frameHeight = 1000
val frameBackgroundColour = Color.white
val frame = Frame.size(frameWidth,frameHeight)
.title(frameTitle)
.background(frameBackgroundColour)
image.draw(frame)
def queens(n: Int): List[List[Int]] =
def placeQueens(k: Int): List[List[Int]] =
if k == 0 then List(List())
else
for
queens <- placeQueens(k - 1)
queen <- 1 to n
if safe(queen, queens)
yield queen :: queens
placeQueens(n)
def makePaddedImageGrid[A](as:List[A],makeImage:A => Image,gridWidth:Int):Grid[Image] =
makeImageGrid(as, makeImage, gridWidth) pipe insertPadding
def insertPadding(images: Grid[Image]): Grid[Image] =
import scalaz._, Scalaz._
images map (_ intersperse paddingImage) intersperse List(paddingImage)
We are now finally ready to run the program!
See the next slide for the results.
See the slide after that for the same results, but
annotated with a few comprehension aids.
@philip_schwarz
N-Queens Combinatorial Problem - Polyglot FP for fun and profit - Haskell and Scala - Part 3
92 boards
N = 5
N = 6
N = 4
N = 7
N = 8
40 boards
4 boards
10 boards
2 boards
Remember in Part 2, when we changed the Scala program‘s logic for displaying a
board, so that instead of exploiting Doodle’s ability to automatically position
images relative to each other, by combining them with the beside and above
functions, the logic had to first explicitly position the images by itself, and then
combine the images using the on function?
We did that so that we could then translate the logic from Scala with Doodle to
Haskell with Gloss.
Imagine doing the equivalent in order to display the N-Queens solutions for
N=4,5,6,7,8!
While it could turn out to be relatively challenging, I don’t think that if we did do
it, we would feel a great sense of accomplishment.
While we might come across opportunities to use interesting functional
programming techniques, I can imagine us being assailed by a growing sense
that we are working on a fool’s errand.
Let’s do something more interesting/constructive instead. In Part 4 we are going
to first look at Haskell’s intersperse and intercalate functions, and then see an
alternative way of solving the N-Queens problem, using the foldM function.
I hope you enjoyed that.
See you in Part 4.
@philip_schwarz
1 of 27

Recommended

Simple IO Monad in 'Functional Programming in Scala' by
Simple IO Monad in 'Functional Programming in Scala'Simple IO Monad in 'Functional Programming in Scala'
Simple IO Monad in 'Functional Programming in Scala'Philip Schwarz
373 views4 slides
Monoids - Part 1 - with examples using Scalaz and Cats by
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsPhilip Schwarz
1.7K views39 slides
Functional Core and Imperative Shell - Game of Life Example - Haskell and Scala by
Functional Core and Imperative Shell - Game of Life Example - Haskell and ScalaFunctional Core and Imperative Shell - Game of Life Example - Haskell and Scala
Functional Core and Imperative Shell - Game of Life Example - Haskell and ScalaPhilip Schwarz
825 views13 slides
Monad Fact #2 by
Monad Fact #2Monad Fact #2
Monad Fact #2Philip Schwarz
342 views9 slides
Computer Graphics in Java and Scala - Part 1 by
Computer Graphics in Java and Scala - Part 1Computer Graphics in Java and Scala - Part 1
Computer Graphics in Java and Scala - Part 1Philip Schwarz
403 views25 slides
Sierpinski Triangle - Polyglot FP for Fun and Profit - Haskell and Scala by
Sierpinski Triangle - Polyglot FP for Fun and Profit - Haskell and ScalaSierpinski Triangle - Polyglot FP for Fun and Profit - Haskell and Scala
Sierpinski Triangle - Polyglot FP for Fun and Profit - Haskell and ScalaPhilip Schwarz
985 views49 slides

More Related Content

What's hot

The Functional Programming Triad of Folding, Scanning and Iteration - a first... by
The Functional Programming Triad of Folding, Scanning and Iteration - a first...The Functional Programming Triad of Folding, Scanning and Iteration - a first...
The Functional Programming Triad of Folding, Scanning and Iteration - a first...Philip Schwarz
105 views31 slides
Abstracting over the Monad yielded by a for comprehension and its generators by
Abstracting over the Monad yielded by a for comprehension and its generatorsAbstracting over the Monad yielded by a for comprehension and its generators
Abstracting over the Monad yielded by a for comprehension and its generatorsPhilip Schwarz
259 views4 slides
Game of Life - Polyglot FP - Haskell, Scala, Unison - Part 2 - with minor cor... by
Game of Life - Polyglot FP - Haskell, Scala, Unison - Part 2 - with minor cor...Game of Life - Polyglot FP - Haskell, Scala, Unison - Part 2 - with minor cor...
Game of Life - Polyglot FP - Haskell, Scala, Unison - Part 2 - with minor cor...Philip Schwarz
442 views55 slides
Oh, All the things you'll traverse by
Oh, All the things you'll traverseOh, All the things you'll traverse
Oh, All the things you'll traverseLuka Jacobowitz
1K views46 slides
Applicative Functor - Part 3 by
Applicative Functor - Part 3Applicative Functor - Part 3
Applicative Functor - Part 3Philip Schwarz
648 views38 slides
Functors by
FunctorsFunctors
FunctorsPhilip Schwarz
1K views5 slides

What's hot(20)

The Functional Programming Triad of Folding, Scanning and Iteration - a first... by Philip Schwarz
The Functional Programming Triad of Folding, Scanning and Iteration - a first...The Functional Programming Triad of Folding, Scanning and Iteration - a first...
The Functional Programming Triad of Folding, Scanning and Iteration - a first...
Philip Schwarz105 views
Abstracting over the Monad yielded by a for comprehension and its generators by Philip Schwarz
Abstracting over the Monad yielded by a for comprehension and its generatorsAbstracting over the Monad yielded by a for comprehension and its generators
Abstracting over the Monad yielded by a for comprehension and its generators
Philip Schwarz259 views
Game of Life - Polyglot FP - Haskell, Scala, Unison - Part 2 - with minor cor... by Philip Schwarz
Game of Life - Polyglot FP - Haskell, Scala, Unison - Part 2 - with minor cor...Game of Life - Polyglot FP - Haskell, Scala, Unison - Part 2 - with minor cor...
Game of Life - Polyglot FP - Haskell, Scala, Unison - Part 2 - with minor cor...
Philip Schwarz442 views
Applicative Functor - Part 3 by Philip Schwarz
Applicative Functor - Part 3Applicative Functor - Part 3
Applicative Functor - Part 3
Philip Schwarz648 views
Scala as a Declarative Language by vsssuresh
Scala as a Declarative LanguageScala as a Declarative Language
Scala as a Declarative Language
vsssuresh3.2K views
Reasoning about laziness by Johan Tibell
Reasoning about lazinessReasoning about laziness
Reasoning about laziness
Johan Tibell3.7K views
Scala collection methods flatMap and flatten are more powerful than monadic f... by Philip Schwarz
Scala collection methods flatMap and flatten are more powerful than monadic f...Scala collection methods flatMap and flatten are more powerful than monadic f...
Scala collection methods flatMap and flatten are more powerful than monadic f...
Philip Schwarz1.7K views
Introducing Assignment invalidates the Substitution Model of Evaluation and v... by Philip Schwarz
Introducing Assignment invalidates the Substitution Model of Evaluation and v...Introducing Assignment invalidates the Substitution Model of Evaluation and v...
Introducing Assignment invalidates the Substitution Model of Evaluation and v...
Philip Schwarz623 views
One Monad to Rule Them All by John De Goes
One Monad to Rule Them AllOne Monad to Rule Them All
One Monad to Rule Them All
John De Goes4.6K views
Scala. Introduction to FP. Monads by Kirill Kozlov
Scala. Introduction to FP. MonadsScala. Introduction to FP. Monads
Scala. Introduction to FP. Monads
Kirill Kozlov1.1K views
Function Composition - forward composition versus backward composition by Philip Schwarz
Function Composition - forward composition versus backward compositionFunction Composition - forward composition versus backward composition
Function Composition - forward composition versus backward composition
Philip Schwarz688 views
Introduction to Monads in Scala (2) by stasimus
Introduction to Monads in Scala (2)Introduction to Monads in Scala (2)
Introduction to Monads in Scala (2)
stasimus6.1K views
Scala Functional Patterns by league
Scala Functional PatternsScala Functional Patterns
Scala Functional Patterns
league1.4K views
Fp in scala part 2 by Hang Zhao
Fp in scala part 2Fp in scala part 2
Fp in scala part 2
Hang Zhao995 views

Similar to N-Queens Combinatorial Problem - Polyglot FP for fun and profit - Haskell and Scala - Part 3

Computer Graphics in Java and Scala - Part 1b by
Computer Graphics in Java and Scala - Part 1bComputer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1bPhilip Schwarz
261 views28 slides
Background This course is all about data visualization. However, we.docx by
Background This course is all about data visualization. However, we.docxBackground This course is all about data visualization. However, we.docx
Background This course is all about data visualization. However, we.docxrosemaryralphs52525
2 views9 slides
Eigenfaces by
EigenfacesEigenfaces
EigenfacesThiwanka Makumburage
396 views10 slides
Matlab intro by
Matlab introMatlab intro
Matlab introfvijayami
1.3K views35 slides
Monad Transformers - Part 1 by
Monad Transformers - Part 1Monad Transformers - Part 1
Monad Transformers - Part 1Philip Schwarz
718 views28 slides
asmt7~$sc_210_-_assignment_7_fall_15.docasmt7cosc_210_-_as.docx by
asmt7~$sc_210_-_assignment_7_fall_15.docasmt7cosc_210_-_as.docxasmt7~$sc_210_-_assignment_7_fall_15.docasmt7cosc_210_-_as.docx
asmt7~$sc_210_-_assignment_7_fall_15.docasmt7cosc_210_-_as.docxfredharris32
4 views98 slides

Similar to N-Queens Combinatorial Problem - Polyglot FP for fun and profit - Haskell and Scala - Part 3(20)

Computer Graphics in Java and Scala - Part 1b by Philip Schwarz
Computer Graphics in Java and Scala - Part 1bComputer Graphics in Java and Scala - Part 1b
Computer Graphics in Java and Scala - Part 1b
Philip Schwarz261 views
Background This course is all about data visualization. However, we.docx by rosemaryralphs52525
Background This course is all about data visualization. However, we.docxBackground This course is all about data visualization. However, we.docx
Background This course is all about data visualization. However, we.docx
Matlab intro by fvijayami
Matlab introMatlab intro
Matlab intro
fvijayami1.3K views
asmt7~$sc_210_-_assignment_7_fall_15.docasmt7cosc_210_-_as.docx by fredharris32
asmt7~$sc_210_-_assignment_7_fall_15.docasmt7cosc_210_-_as.docxasmt7~$sc_210_-_assignment_7_fall_15.docasmt7cosc_210_-_as.docx
asmt7~$sc_210_-_assignment_7_fall_15.docasmt7cosc_210_-_as.docx
fredharris324 views
Codeware by Uri Nativ
CodewareCodeware
Codeware
Uri Nativ66.9K views
Programming in matlab lesson5 by najmah17
Programming in matlab lesson5Programming in matlab lesson5
Programming in matlab lesson5
najmah17598 views
ImplementDijkstra’s algorithm using the graph class you implemente.pdf by gopalk44
ImplementDijkstra’s algorithm using the graph class you implemente.pdfImplementDijkstra’s algorithm using the graph class you implemente.pdf
ImplementDijkstra’s algorithm using the graph class you implemente.pdf
gopalk442 views
Chapter 1: Linear Regression by AkmelSyed
Chapter 1: Linear RegressionChapter 1: Linear Regression
Chapter 1: Linear Regression
AkmelSyed40 views
Matlab practice by ZunAib Ali
Matlab practiceMatlab practice
Matlab practice
ZunAib Ali4.1K views
XIX PUG-PE - Pygame game development by matheuscmpm
XIX PUG-PE - Pygame game developmentXIX PUG-PE - Pygame game development
XIX PUG-PE - Pygame game development
matheuscmpm1.8K views
Creating an Uber Clone - Part IV - Transcript.pdf by ShaiAlmog1
Creating an Uber Clone - Part IV - Transcript.pdfCreating an Uber Clone - Part IV - Transcript.pdf
Creating an Uber Clone - Part IV - Transcript.pdf
ShaiAlmog1287 views
Please make the complete program, Distinguish between header files a.pdf by SALES97
Please make the complete program, Distinguish between header files a.pdfPlease make the complete program, Distinguish between header files a.pdf
Please make the complete program, Distinguish between header files a.pdf
SALES972 views
Let’s Talk About Ruby by Ian Bishop
Let’s Talk About RubyLet’s Talk About Ruby
Let’s Talk About Ruby
Ian Bishop649 views
# Imports# Include your imports here, if any are used. import.pdf by gulshan16175gs
 # Imports# Include your imports here, if any are used. import.pdf # Imports# Include your imports here, if any are used. import.pdf
# Imports# Include your imports here, if any are used. import.pdf
gulshan16175gs10 views
C# Summer course - Lecture 2 by mohamedsamyali
C# Summer course - Lecture 2C# Summer course - Lecture 2
C# Summer course - Lecture 2
mohamedsamyali553 views

More from Philip Schwarz

Scala Left Fold Parallelisation - Three Approaches by
Scala Left Fold Parallelisation- Three ApproachesScala Left Fold Parallelisation- Three Approaches
Scala Left Fold Parallelisation - Three ApproachesPhilip Schwarz
41 views44 slides
Tagless Final Encoding - Algebras and Interpreters and also Programs by
Tagless Final Encoding - Algebras and Interpreters and also ProgramsTagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also ProgramsPhilip Schwarz
45 views16 slides
Fusing Transformations of Strict Scala Collections with Views by
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with ViewsPhilip Schwarz
20 views28 slides
A sighting of sequence function in Practical FP in Scala by
A sighting of sequence function in Practical FP in ScalaA sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in ScalaPhilip Schwarz
28 views4 slides
N-Queens Combinatorial Puzzle meets Cats by
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets CatsPhilip Schwarz
33 views386 slides
Kleisli composition, flatMap, join, map, unit - implementation and interrelat... by
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Philip Schwarz
12 views16 slides

More from Philip Schwarz(20)

Scala Left Fold Parallelisation - Three Approaches by Philip Schwarz
Scala Left Fold Parallelisation- Three ApproachesScala Left Fold Parallelisation- Three Approaches
Scala Left Fold Parallelisation - Three Approaches
Philip Schwarz41 views
Tagless Final Encoding - Algebras and Interpreters and also Programs by Philip Schwarz
Tagless Final Encoding - Algebras and Interpreters and also ProgramsTagless Final Encoding - Algebras and Interpreters and also Programs
Tagless Final Encoding - Algebras and Interpreters and also Programs
Philip Schwarz45 views
Fusing Transformations of Strict Scala Collections with Views by Philip Schwarz
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with Views
Philip Schwarz20 views
A sighting of sequence function in Practical FP in Scala by Philip Schwarz
A sighting of sequence function in Practical FP in ScalaA sighting of sequence function in Practical FP in Scala
A sighting of sequence function in Practical FP in Scala
Philip Schwarz28 views
N-Queens Combinatorial Puzzle meets Cats by Philip Schwarz
N-Queens Combinatorial Puzzle meets CatsN-Queens Combinatorial Puzzle meets Cats
N-Queens Combinatorial Puzzle meets Cats
Philip Schwarz33 views
Kleisli composition, flatMap, join, map, unit - implementation and interrelat... by Philip Schwarz
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Kleisli composition, flatMap, join, map, unit - implementation and interrelat...
Philip Schwarz12 views
The aggregate function - from sequential and parallel folds to parallel aggre... by Philip Schwarz
The aggregate function - from sequential and parallel folds to parallel aggre...The aggregate function - from sequential and parallel folds to parallel aggre...
The aggregate function - from sequential and parallel folds to parallel aggre...
Philip Schwarz263 views
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske... by Philip Schwarz
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
The Sieve of Eratosthenes - Part II - Genuine versus Unfaithful Sieve - Haske...
Philip Schwarz250 views
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha... by Philip Schwarz
Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...Sum and Product Types -The Fruit Salad & Fruit Snack Example - From F# to Ha...
Sum and Product Types - The Fruit Salad & Fruit Snack Example - From F# to Ha...
Philip Schwarz827 views
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t... by Philip Schwarz
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Philip Schwarz1.2K views
Jordan Peterson - The pursuit of meaning and related ethical axioms by Philip Schwarz
Jordan Peterson - The pursuit of meaning and related ethical axiomsJordan Peterson - The pursuit of meaning and related ethical axioms
Jordan Peterson - The pursuit of meaning and related ethical axioms
Philip Schwarz141 views
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c... by Philip Schwarz
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Defining filter using (a) recursion (b) folding (c) folding with S, B and I c...
Philip Schwarz100 views
Defining filter using (a) recursion (b) folding with S, B and I combinators (... by Philip Schwarz
Defining filter using (a) recursion (b) folding with S, B and I combinators (...Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Defining filter using (a) recursion (b) folding with S, B and I combinators (...
Philip Schwarz78 views
The Sieve of Eratosthenes - Part 1 - with minor corrections by Philip Schwarz
The Sieve of Eratosthenes - Part 1 - with minor correctionsThe Sieve of Eratosthenes - Part 1 - with minor corrections
The Sieve of Eratosthenes - Part 1 - with minor corrections
Philip Schwarz110 views
The Sieve of Eratosthenes - Part 1 by Philip Schwarz
The Sieve of Eratosthenes - Part 1The Sieve of Eratosthenes - Part 1
The Sieve of Eratosthenes - Part 1
Philip Schwarz1K views
The Uniform Access Principle by Philip Schwarz
The Uniform Access PrincipleThe Uniform Access Principle
The Uniform Access Principle
Philip Schwarz542 views
The Expression Problem - Part 2 by Philip Schwarz
The Expression Problem - Part 2The Expression Problem - Part 2
The Expression Problem - Part 2
Philip Schwarz650 views
The Expression Problem - Part 1 by Philip Schwarz
The Expression Problem - Part 1The Expression Problem - Part 1
The Expression Problem - Part 1
Philip Schwarz1.2K views
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac... by Philip Schwarz
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Philip Schwarz268 views
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ... by Philip Schwarz
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...
Refactoring: A First Example - Martin Fowler’s First Example of Refactoring, ...
Philip Schwarz312 views

Recently uploaded

DSD-INT 2023 Thermobaricity in 3D DCSM-FM - taking pressure into account in t... by
DSD-INT 2023 Thermobaricity in 3D DCSM-FM - taking pressure into account in t...DSD-INT 2023 Thermobaricity in 3D DCSM-FM - taking pressure into account in t...
DSD-INT 2023 Thermobaricity in 3D DCSM-FM - taking pressure into account in t...Deltares
9 views26 slides
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx by
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptxanimuscrm
14 views19 slides
360 graden fabriek by
360 graden fabriek360 graden fabriek
360 graden fabriekinfo33492
38 views25 slides
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J... by
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...Deltares
9 views24 slides
Programming Field by
Programming FieldProgramming Field
Programming Fieldthehardtechnology
5 views9 slides
FIMA 2023 Neo4j & FS - Entity Resolution.pptx by
FIMA 2023 Neo4j & FS - Entity Resolution.pptxFIMA 2023 Neo4j & FS - Entity Resolution.pptx
FIMA 2023 Neo4j & FS - Entity Resolution.pptxNeo4j
7 views26 slides

Recently uploaded(20)

DSD-INT 2023 Thermobaricity in 3D DCSM-FM - taking pressure into account in t... by Deltares
DSD-INT 2023 Thermobaricity in 3D DCSM-FM - taking pressure into account in t...DSD-INT 2023 Thermobaricity in 3D DCSM-FM - taking pressure into account in t...
DSD-INT 2023 Thermobaricity in 3D DCSM-FM - taking pressure into account in t...
Deltares9 views
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx by animuscrm
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx
2023-November-Schneider Electric-Meetup-BCN Admin Group.pptx
animuscrm14 views
360 graden fabriek by info33492
360 graden fabriek360 graden fabriek
360 graden fabriek
info3349238 views
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J... by Deltares
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
DSD-INT 2023 3D hydrodynamic modelling of microplastic transport in lakes - J...
Deltares9 views
FIMA 2023 Neo4j & FS - Entity Resolution.pptx by Neo4j
FIMA 2023 Neo4j & FS - Entity Resolution.pptxFIMA 2023 Neo4j & FS - Entity Resolution.pptx
FIMA 2023 Neo4j & FS - Entity Resolution.pptx
Neo4j7 views
DSD-INT 2023 Delft3D FM Suite 2024.01 2D3D - New features + Improvements - Ge... by Deltares
DSD-INT 2023 Delft3D FM Suite 2024.01 2D3D - New features + Improvements - Ge...DSD-INT 2023 Delft3D FM Suite 2024.01 2D3D - New features + Improvements - Ge...
DSD-INT 2023 Delft3D FM Suite 2024.01 2D3D - New features + Improvements - Ge...
Deltares17 views
DSD-INT 2023 Leveraging the results of a 3D hydrodynamic model to improve the... by Deltares
DSD-INT 2023 Leveraging the results of a 3D hydrodynamic model to improve the...DSD-INT 2023 Leveraging the results of a 3D hydrodynamic model to improve the...
DSD-INT 2023 Leveraging the results of a 3D hydrodynamic model to improve the...
Deltares6 views
Advanced API Mocking Techniques by Dimpy Adhikary
Advanced API Mocking TechniquesAdvanced API Mocking Techniques
Advanced API Mocking Techniques
Dimpy Adhikary19 views
DSD-INT 2023 Delft3D FM Suite 2024.01 1D2D - Beta testing programme - Geertsema by Deltares
DSD-INT 2023 Delft3D FM Suite 2024.01 1D2D - Beta testing programme - GeertsemaDSD-INT 2023 Delft3D FM Suite 2024.01 1D2D - Beta testing programme - Geertsema
DSD-INT 2023 Delft3D FM Suite 2024.01 1D2D - Beta testing programme - Geertsema
Deltares17 views
Copilot Prompting Toolkit_All Resources.pdf by Riccardo Zamana
Copilot Prompting Toolkit_All Resources.pdfCopilot Prompting Toolkit_All Resources.pdf
Copilot Prompting Toolkit_All Resources.pdf
Riccardo Zamana8 views
DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h... by Deltares
DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h...DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h...
DSD-INT 2023 Exploring flash flood hazard reduction in arid regions using a h...
Deltares5 views
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ... by Deltares
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...
DSD-INT 2023 Wave-Current Interaction at Montrose Tidal Inlet System and Its ...
Deltares11 views
SUGCON ANZ Presentation V2.1 Final.pptx by Jack Spektor
SUGCON ANZ Presentation V2.1 Final.pptxSUGCON ANZ Presentation V2.1 Final.pptx
SUGCON ANZ Presentation V2.1 Final.pptx
Jack Spektor22 views
Myths and Facts About Hospice Care: Busting Common Misconceptions by Care Coordinations
Myths and Facts About Hospice Care: Busting Common MisconceptionsMyths and Facts About Hospice Care: Busting Common Misconceptions
Myths and Facts About Hospice Care: Busting Common Misconceptions
Quality Engineer: A Day in the Life by John Valentino
Quality Engineer: A Day in the LifeQuality Engineer: A Day in the Life
Quality Engineer: A Day in the Life
John Valentino6 views
Sprint 226 by ManageIQ
Sprint 226Sprint 226
Sprint 226
ManageIQ5 views

N-Queens Combinatorial Problem - Polyglot FP for fun and profit - Haskell and Scala - Part 3

  • 1. N-Queens Combinatorial Problem Learn how to write FP code that displays a graphical representation of all the numerous N-Queens solutions for N=4,5,6,7,8 See how to neatly solve the problem by exploiting its self-similarity and using a divide and conquer approach Make light work of assembling multiple images into a whole, by exploiting Doodle’s facilities for combining images using a relative layout See relevant FP functions, like Foldable’s intercalate and intersperse, in action @philip_schwarz slides by https://www.slideshare.net/pjschwarz Part 3 Doodle Polyglot FP for Fun and Profit – Haskell and Scala
  • 2. Welcome to Part 3 of this series. In this part, we are going to write a new program that displays, all together, the results of queens(N) for N = 4, 5, 6, 7, 8. The next slide shows both the program (from Part 2) that displays the board for a single solution, and the beginnings of the new program, which will reuse some logic from both Part 1 and Part 2.
  • 3. def display(ns: List[Int])(image: Image): Unit = val frameTitle = "N-Queens Problem - Solutions for N = ${ns.mkString(",")}" val frameWidth = 1800 val frameHeight = 1000 val frameBackgroundColour = Color.white val frame = Frame.size(frameWidth,frameHeight) .title(frameTitle) .background(frameBackgroundColour) image.draw(frame) @main def main = val ns = List(4,5,6,7,8) ns map queens pipe makeResultsImage pipe display(ns) val makeResultsImage: List[List[List[Int]]] => Image = ??? // to be implemented def showQueens(solution: List[Int]): Int = val n = solution.length val frameTitle = s"{n}-Queens Problem – A solution" val frameWidth = 1000 val frameHeight = 1000 val frameBackgroundColour = Color.white val frame = Frame.size(frameWidth,frameHeight) .title(frameTitle) .background(frameBackgroundColour) show(solution).draw(frame) def show(queens: List[Int]): Image = val square = Image.square(100).strokeColor(Color.black) val emptySquare: Image = square.fillColor(Color.white) val fullSquare: Image = square.fillColor(Color.orangeRed) val squareImageGrid: List[List[Image]] = for col <- queens.reverse yield List.fill(queens.length)(emptySquare) .updated(col,fullSquare) combine(squareImageGrid) val beside = Monoid.instance[Image](Image.empty, _ beside _) val above = Monoid.instance[Image](Image.empty, _ above _) def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll beside)(above) @main def main = val solution = List(3,1,6,2,5,7,4,0) showQueens(solution) def onDiagonal(row: Int, column: Int, otherRow: Int, otherColumn: Int) = math.abs(row - otherRow) == math.abs(column - otherColumn) def safe(queen: Int, queens: List[Int]): Boolean = val (row, column) = (queens.length, queen) val safe: ((Int,Int)) => Boolean = (nextRow, nextColumn) => column != nextColumn && !onDiagonal(column, row, nextColumn, nextRow) zipWithRows(queens) forall safe def zipWithRows(queens: List[Int]): Iterable[(Int,Int)] = val rowCount = queens.length val rowNumbers = rowCount - 1 to 0 by -1 rowNumbers zip queens def queens(n: Int): List[List[Int]] = def placeQueens(k: Int): List[List[Int]] = if k == 0 then List(List()) else for queens <- placeQueens(k - 1) queen <- 1 to n if safe(queen, queens) yield queen :: queens placeQueens(n) We are switching from the program on the left, to the one on the right. New code is on a green background. In the new program, generating an image is the responsibility of makeResultsImage. See next slide for an explanation of pipe. The program on the left is from Part 2. It displays the board for a single solution. The program on the right uses the queens function (and ancillary functions) from Part 1. It displays, all together, the results of queens(N) for N = 4, 5, 6, 7, 8.
  • 4. Scala’s pipe function allows us to take an expression consisting of a number of nested function invocations, e.g. f(g(h(x)), and turn it into an equivalent expression in which the functions appear in the order in which they are invoked, i.e. h, g and f, rather than in the inverse order, i.e. f, g and h. assert(square(twice(inc(3))) == 64) assert ((3 pipe inc pipe twice pipe square) == 64) def inc(n: Int): Int = n + 1 def twice(n: Int): Int = n * 2 def square(n: Int): Int = n * n @philip_schwarz Here is one example @main def main = val ns = List(4,5,6,7,8) display(ns)(makeResultsImage(ns map queens)) @main def main = val ns = List(4,5,6,7,8) ns map queens pipe makeResultsImage pipe display(ns) We are using pipe to make our main function easier to understand
  • 5. @main def main = val ns = List(4,5,6,7,8) ns map queens pipe makeResultsImage pipe display(ns) val makeResultsImage: List[List[List[Int]]] => Image = ??? type Solution = List[Int] val makeResultsImage: List[List[Solution]] => Image = ??? val makeResultsImage: List[Solutions] => Image = ??? type Solutions = List[Solution] Our current objective is to implement the makeResultsImage function invoked by main. Let’s begin by introducing a couple of type aliases to aid comprehension.
  • 6. If at some point, while reading the next three slides, you feel a strong sense of déjà vu, that is to be expected. There is a lot of symmetry between the slides, and the code that they contain. That’s because the problem that we are working on exhibits a good degree of self-similarity. The problem looks very similar at three different levels: • When we operate at the single Solution level, we need to create an image of a grid of squares (a solution board). • When we operate at the multiple Solution level, we need to create an image of a grid of boards (the solution boards for some N). • When we operate at the multiple Solutions level, we need to create an image of a grid of grids of boards (i.e. all the solution boards for N=4,5,6,7,8). If things appear to get a bit confusing at times, keep a cool head by focusing on the function signatures at play, and by reminding yourself that all we are doing is divide and conquer. type Solution = List[Int] type Solutions = List[Solution]
  • 7. val makeResultsImage: List[Solutions] => Image = makeSolutionsImageGrid andThen combineWithPadding type Grid[A] = List[List[A]] type Solution = List[Int] type Solutions = List[Solution] makeSolutionsImageGrid : List[Solutions] => Grid[Image] combineWithPadding : Grid[Image] => Image To turn multiple Solutions elements into an image, we are going to first create an Image for each Solutions element, then arrange the resulting images in a Grid, and finally combine the images into a single compound image, adding padding around images as we combine them. Creating the images, and arranging them into a grid, will be done by makeSolutionsImageGrid, whereas combining the images into a single compound image, inserting padding around the images, will be done by combineWithPadding. In order to create a Grid[Image], makeSolutionsImageGrid must create an image for each Solutions element. To help with that, on the next slide we define a function called makeSolutionsImage, which given a Solutions element, returns an Image. This is analogous to makeResultsImage, but operates on an individual Solutions element rather than on a list of such elements, so it operates one level below makeResultsImage.
  • 8. val makeSolutionsImage: List[Solution] => Image = makeBoardImageGrid andThen combineWithPadding To turn multiple Solution elements into an image, we are going to first create an Image for each Solution, then arrange the images in a Grid, and finally combine the images into a single compound image, adding padding around images as we combine them. Creating the images, and arranging them into a grid, will be done by makeBoardImageGrid, whereas combining the images into a single compound image, inserting padding around the images, will be done by combineWithPadding (yes, we introduced it on the previous slide). makeBoardImageGrid : List[Solution] => Grid[Image] combineWithPadding : Grid[Image] => Image In order to create a Grid[Image], makeBoardImageGrid must create an image for each Solution element. To help with that, on the next slide we define a function called makeBoardImage, which given a Solution element, returns an Image. This is analogous to makeSolutionsImage, but operates on an individual Solution element rather than on a list of such elements, so it operates one level below makeSolutionsImage. type Grid[A] = List[List[A]] type Solution = List[Int] type Solutions = List[Solution]
  • 9. val makeBoardImage: Solution => Image = makeSquareImageGrid andThen combine To turn a Solution, which represents a chess board, into an image, we are going to first create an Image for each square in the Solution, then arrange the images in a Grid, and finally combine the images into a single compound image. Creating the images, and arranging them into a grid, will be done by makeSquareImageGrid, whereas combining the images into a single compound image will be done by combine (yes, we implemented such a function in Part 2). makeSquareImageGrid : Solution => Grid[Image] combine : Grid[Image] => Image type Grid[A] = List[List[A]] type Solution = List[Int] type Solutions = List[Solution] The next slide visualises the self-similarity of the problem we are working on, and its amenability to a divide and conquer approach. @philip_schwarz
  • 10. a cell in a board a single solution for N = n a board (solution) all solutions for N = n all solutions for N = n all solutions for N = n, n+1, n+2, n+3, n+4
  • 11. val makeResultsImage: List[Solutions] => Image = makeSolutionsImageGrid andThen combineWithPadding makeSolutionsImageGrid: List[Solutions] => Grid[Image] combineWithPadding: Grid[Image] => Image val makeSolutionsImage: List[Solution] => Image = makeBoardImageGrid andThen combineWithPadding makeBoardImageGrid: List[Solution] => Grid[Image] val makeBoardImage: Solution => Image = makeSquareImageGrid andThen combine makeSquareImageGrid: Solution => Grid[Image] combine: Grid[Image] => Image Combine a grid of images into a composite image with padding around the images Create an image of a grid of squares (a solution board) Create an image of a grid of boards (the solution boards for some N) Create an image of a grid of grids of boards (i.e. all the solution boards for N=4,5,6,7,8) Create a grid of square images (a solution board) Create a grid of board images (the solution boards for some N) Create a grid of images of grids of boards (i.e. all the solution boards for N=4,5,6,7,8) Combine a grid of images into a composite image with no padding around the images Here are the functions that we have identified so far. We already have implementations for the first three functions. On the next slide, we start implementing the next three functions, which create grids of images. type Grid[A] = List[List[A]] type Solution = List[Int] type Solutions = List[Solution]
  • 12. makeSolutionsImageGrid: List[Solutions] => Grid[Image] makeBoardImageGrid: List[Solution] => Grid[Image] makeSquareImageGrid: Solution => Grid[Image] Since all three of these functions have to create a grid, they will have some logic in common. Let’s put that shared logic in a function called makeImageGrid. def makeImageGrid[A](as: List[A], makeImage: A => Image, gridWidth: Int): Grid[Image] = as map makeImage grouped gridWidth toList Implementing makeSolutionsImageGrid and makeBoardImageGrid is now simply a matter of invoking makeImageGrid. def makeSolutionsImageGrid(queensResults: List[Solutions]): Grid[Image] = makeImageGrid(queensResults, makeSolutionsImage, gridWidth = 1) def makeBoardImageGrid(solutions: List[Solution]): Grid[Image] = makeImageGrid(solutions, makeBoardImage, gridWidth = 17) See the previous slide for implementations of makeSolutionsImage and makeBoardImage. We are creating a degenerate grid, one with just 1 column. We are doing so simply because it happens to result in an effective layout. Again, it just happens that creating a grid with 17 columns results in a better layout of solution boards than is otherwise the case. type Grid[A] = List[List[A]] type Solution = List[Int] type Solutions = List[Solution]
  • 13. While Implementing makeSolutionsImageGrid and makeBoardImageGrid was simply a matter of invoking makeImageGrid, implementing makeSquareImageGrid is more involved. def makeSquareImageGrid(columnIndices:Solution): Grid[Image] = val n = columnIndices.length val (emptySquare, fullSquare) = makeSquareImages(n) val occupiedCells: List[Boolean] = columnIndices.reverse flatMap { col => List.fill(n)(false).updated(col-1,true) } val makeSquareImage: Boolean => Image = if (_) fullSquare else emptySquare makeImageGrid(occupiedCells, makeSquareImage, gridWidth = n) def makeSquareImages(n: Int): (Image,Image) = val emptySquareColour = n match case 4 => Color.limeGreen case 5 => Color.lime case 6 => Color.springGreen case 7 => Color.paleGreen case 8 => Color.greenYellow case other => Color.white val square: Image = Image.square(10).strokeColor(Color.black) val emptySquare: Image = square.fillColor(emptySquareColour) val fullSquare: Image = square.fillColor(Color.orangeRed) (emptySquare, fullSquare) To help distinguish the solution boards for different values of N (4,5,6,7,8), we are giving their empty squares of a different shade of green.
  • 14. val makeResultsImage: List[Solutions] => Image = makeSolutionsImageGrid andThen combineWithPadding combineWithPadding: Grid[Image] => Image val makeSolutionsImage: List[Solution] => Image = makeBoardImageGrid andThen combineWithPadding val makeBoardImage: Solution => Image = makeSquareImageGrid andThen combine combine: Grid[Image] => Image Looking back at the implementations of our three functions for creating images, now that we have implemented the functions that create image grids, it is time to implement combine and combineWithPadding. On the next slide we start implementing combineWithPadding. @philip_schwarz
  • 15. import cats.Monoid val beside = Monoid.instance[Image](Image.empty, _ beside _) val above = Monoid.instance[Image](Image.empty, _ above _) def combine(imageGrid: List[List[Image]]): Image = import cats.implicits._ imageGrid.foldMap(_ combineAll beside)(above) Remember the implementation of the combine function that we used, in Part 2, to take a grid of images and produce a compound image that is their composition? We need to implement combineWithPadding, a function that differs from combine in that instead of just combining the images contained in its image grid parameter, it also needs to insert a padding image between neighbouring images as it does that. combineWithPadding: Grid[Image] => Image The combine function first folds the images in a row (combineAll is just an alias for fold) using the beside monoid, and then folds the resulting row images using the above monoid. The combineWithPadding function needs to fold images in the same way, but in addition, it also needs to insert a padding image between each pair of such images. This is clearly a job for the intercalate function provided by Cats’ Foldable type class!
  • 16. def combineWithPadding(images: Grid[Image]): Image = combineWithPadding(images, paddingImage) def combineWithPadding(images: Grid[Image], paddingImage: Image): Image = import cats.implicits._ images.map(row => row.intercalate(paddingImage)(beside)) .intercalate(paddingImage)(above) val paddingImage = Image.square(10).strokeColor(Color.white).fillColor(Color.white) Let’s go ahead and implement combineWithPadding using the intercalate function provided by Cats’ Foldable type class! The padding consists of a white square with a width of 10 pixels. def combine(images: Grid[Image]): Image = combineWithPadding(images, paddingImage = Image.empty) def combineWithPadding(images: Grid[Image], paddingImage: Image): Image = import cats.Foldable Foldable[List].intercalate ( images.map(row => Foldable[List].intercalate(row, paddingImage)(beside)), paddingImage )(above) In case you find it useful, here is how the second combineWithPadding function looks like if we use Foldable[List]explicitly. def combine(imageGrid: List[List[Image]]): Image = import cats.implicits._ imageGrid.foldMap(_ combineAll beside)(above) Here again, for reference, is how we implemented combine in Part 2. What about the combine function, which doesn’t do any padding? Why is it that a couple of slides ago we said that we need to implement it? We have already implemented it in Part 2 (see above). While we can certainly just use the above combine function, it is interesting to see how much simpler the implementation becomes if we leverage the combineWithPadding function that we have just introduced. No padding, is just padding with an empty image. A bit silly maybe, but attractively simple.
  • 17. The next slide shows all of the code needed to display the N-Queens solutions for N=4,5,6,7,8. While the queens function is included on the slide, this is purely to remind us of how the solutions are produced, and so its three subordinate functions are not shown. @philip_schwarz
  • 18. def combine(images: Grid[Image]): Image = combineWithPadding(images, paddingImage = Image.empty) def combineWithPadding(images: Grid[Image]): Image = combineWithPadding(images, paddingImage) def combineWithPadding(images: Grid[Image], paddingImage: Image): Image = import cats.implicits._ images.map(row => row.intercalate(paddingImage)(beside)) .intercalate(paddingImage)(above) import cats.Monoid val beside = Monoid.instance[Image](Image.empty, _ beside _) val above = Monoid.instance[Image](Image.empty, _ above _) val paddingImage = Image.square(10) .strokeColor(Color.white) .fillColor(Color.white) val makeResultsImage: List[Solutions] => Image = makeSolutionsImageGrid andThen combineWithPadding val makeSolutionsImage: List[Solution] => Image = makeBoardImageGrid andThen combineWithPadding val makeBoardImage: Solution => Image = makeSquareImageGrid andThen combine def makeSquareImageGrid(columnIndices:Solution): Grid[Image] = val n = columnIndices.length val (emptySquare, fullSquare) = makeSquareImages(n) val occupiedCells: List[Boolean] = columnIndices.reverse flatMap { col => List.fill(n)(false).updated(col-1,true) } val makeSquareImage: Boolean => Image = if (_) fullSquare else emptySquare makeImageGrid(occupiedCells, makeSquareImage, gridWidth = n) def makeSquareImages(n: Int): (Image,Image) = val emptySquareColour = n match case 4 => Color.limeGreen case 5 => Color.lime case 6 => Color.springGreen case 7 => Color.paleGreen case 8 => Color.greenYellow case other => Color.white val square: Image = Image.square(10).strokeColor(Color.black) val emptySquare: Image = square.fillColor(emptySquareColour) val fullSquare: Image = square.fillColor(Color.orangeRed) (emptySquare, fullSquare) def makeImageGrid[A](as: List[A], makeImage: A => Image, gridWidth: Int): Grid[Image] = as map makeImage grouped gridWidth toList def makeSolutionsImageGrid(queensResults: List[Solutions]): Grid[Image] = makeImageGrid(queensResults, makeSolutionsImage, gridWidth = 1) def makeBoardImageGrid(solutions: List[Solution]): Grid[Image] = makeImageGrid(solutions, makeBoardImage, gridWidth = 17) type Grid[A] = List[List[A]] type Solution = List[Int] type Solutions = List[Solution] @main def main = val ns = List(4,5,6,7,8) ns map queens pipe makeResultsImage pipe display(ns) def display(ns: List[Int])(image: Image): Unit = val frameTitle = "N-Queens Problem - Solutions for N = ${ns.mkString(",")}" val frameWidth = 1800 val frameHeight = 1000 val frameBackgroundColour = Color.white val frame = Frame.size(frameWidth,frameHeight) .title(frameTitle) .background(frameBackgroundColour) image.draw(frame) def queens(n: Int): List[List[Int]] = def placeQueens(k: Int): List[List[Int]] = if k == 0 then List(List()) else for queens <- placeQueens(k - 1) queen <- 1 to n if safe(queen, queens) yield queen :: queens placeQueens(n)
  • 19. While the code on the previous slide works, I think the combineWithPadding function is doing too much. It is unnecessarily conflating two responsibilities: inserting padding between images, and combining the images. Let’s delete the combine and combineWithPadding functions on the left, and reinstate the earlier combine function, on the right. def combine(images: Grid[Image]): Image = combineWithPadding(images, paddingImage = Image.empty) def combineWithPadding(images: Grid[Image]): Image = combineWithPadding(images, paddingImage) def combineWithPadding(images: Grid[Image], paddingImage: Image): Image = import cats.implicits._ images.map(row => row.intercalate(paddingImage)(beside)) .intercalate(paddingImage)(above) def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll beside)(above) val makeResultsImage: List[Solutions] => Image = makeSolutionsImageGrid andThen combineWithPadding val makeSolutionsImage: List[Solution] => Image = makeBoardImageGrid andThen combineWithPadding val makeResultsImage: List[Solutions] => Image = makeSolutionsImageGrid andThen combine val makeSolutionsImage: List[Solution] => Image = makeBoardImageGrid andThen combine We must now stop makeResultsImage and makeSolutionsImage from using the combineWithPadding function that we have deleted. Now that padding no longer gets introduced when images are combined, when is it going to be introduced? See the next slide.
  • 20. import scala.collection.decorators._ def insertPadding(images: Grid[Image]): Grid[Image] = images map (_ intersperse paddingImage) intersperse List(paddingImage) Let’s define a function called insertPadding, that takes a grid of images, and inserts a padding image between each pair of neighbouring images. We can implement the function using the intersperse function provided by https://github.com/scala/scala-collection-contrib. Except that when I try to use the intersperse function with Scala 3, I get a compilation error, so I have opened an issue: https://github.com/scala/scala-collection-contrib/pull/146.
  • 21. def insertPadding(images: Grid[Image]): Grid[Image] = import scalaz._, Scalaz._ images map (_ intersperse paddingImage) intersperse List(paddingImage) Luckily, the very same intersperse function is also available in Scalaz. def makePaddedImageGrid[A](as: List[A], makeImage: A => Image, gridWidth: Int): Grid[Image] = makeImageGrid(as, makeImage, gridWidth) pipe insertPadding def makeImageGrid[A](as: List[A], makeImage: A => Image, gridWidth: Int): Grid[Image] = as map makeImage grouped gridWidth toList def makeSolutionsImageGrid(queensResults: List[Solutions]): Grid[Image] = makeImageGrid(queensResults, makeSolutionsImage, gridWidth = 1) def makeBoardImageGrid(solutions: List[Solution]): Grid[Image] = makeImageGrid(solutions, makeBoardImage, gridWidth = 17) Now, remember makeImageGrid, the function that we use to turn a list into a grid of images? Now that we have defined insertPadding, we can use it to define a variant of makeImageGrid which, in addition to creating a grid of images, inserts padding between those images. Armed with the above function, we can now remedy the fact that we have eliminated the combineWithPadding function. The padding that was previously inserted by using combineWithPadding, will now be inserted by invoking makePaddedImageGrid rather than makeImageGrid. We need to make the switch in the following two functions: The next slide applies the additions/changes described on this slide, to the code needed to display the N-Queens solutions for N=4,5,6,7,8. List(1, 2, 3) intersperse 0 assert_=== List(1,0,2,0,3) List(1, 2) intersperse 0 assert_=== List(1,0,2) List(1) intersperse 0 assert_=== List(1) nil[Int] intersperse 0 assert_=== nil[Int]
  • 22. def combine(imageGrid: List[List[Image]]): Image = imageGrid.foldMap(_ combineAll beside)(above) import cats.Monoid val beside = Monoid.instance[Image](Image.empty, _ beside _) val above = Monoid.instance[Image](Image.empty, _ above _) val paddingImage = Image.square(10) .strokeColor(Color.white) .fillColor(Color.white) val makeResultsImage: List[Solutions] => Image = makeSolutionsImageGrid andThen combineWithPadding val makeSolutionsImage: List[Solution] => Image = makeBoardImageGrid andThen combineWithPadding val makeBoardImage: Solution => Image = makeSquareImageGrid andThen combine def makeSquareImageGrid(columnIndices:Solution): Grid[Image] = val n = columnIndices.length val (emptySquare, fullSquare) = makeSquareImages(n) val occupiedCells: List[Boolean] = columnIndices.reverse flatMap { col => List.fill(n)(false).updated(col-1,true) } val makeSquareImage: Boolean => Image = if (_) fullSquare else emptySquare makeImageGrid(occupiedCells, makeSquareImage, gridWidth = n) def makeSquareImages(n: Int): (Image,Image) = val emptySquareColour = n match case 4 => Color.limeGreen case 5 => Color.lime case 6 => Color.springGreen case 7 => Color.paleGreen case 8 => Color.greenYellow case other => Color.white val square: Image = Image.square(10).strokeColor(Color.black) val emptySquare: Image = square.fillColor(emptySquareColour) val fullSquare: Image = square.fillColor(Color.orangeRed) (emptySquare, fullSquare) def makeImageGrid[A](as: List[A], makeImage: A => Image, gridWidth: Int): Grid[Image] = as map makeImage grouped gridWidth toList def makeSolutionsImageGrid(queensResults: List[Solutions]): Grid[Image] = makePaddedImageGrid(queensResults, makeSolutionsImage, gridWidth = 1) def makeBoardImageGrid(solutions: List[Solution]): Grid[Image] = makePaddedImageGrid(solutions, makeBoardImage, gridWidth = 17) type Grid[A] = List[List[A]] type Solution = List[Int] type Solutions = List[Solution] @main def main = val ns = List(4,5,6,7,8) ns map queens pipe makeResultsImage pipe display(ns) def display(ns: List[Int])(image: Image): Unit = val frameTitle = "N-Queens Problem - Solutions for N = ${ns.mkString(",")}" val frameWidth = 1800 val frameHeight = 1000 val frameBackgroundColour = Color.white val frame = Frame.size(frameWidth,frameHeight) .title(frameTitle) .background(frameBackgroundColour) image.draw(frame) def queens(n: Int): List[List[Int]] = def placeQueens(k: Int): List[List[Int]] = if k == 0 then List(List()) else for queens <- placeQueens(k - 1) queen <- 1 to n if safe(queen, queens) yield queen :: queens placeQueens(n) def makePaddedImageGrid[A](as:List[A],makeImage:A => Image,gridWidth:Int):Grid[Image] = makeImageGrid(as, makeImage, gridWidth) pipe insertPadding def insertPadding(images: Grid[Image]): Grid[Image] = import scalaz._, Scalaz._ images map (_ intersperse paddingImage) intersperse List(paddingImage)
  • 23. We are now finally ready to run the program! See the next slide for the results. See the slide after that for the same results, but annotated with a few comprehension aids. @philip_schwarz
  • 25. 92 boards N = 5 N = 6 N = 4 N = 7 N = 8 40 boards 4 boards 10 boards 2 boards
  • 26. Remember in Part 2, when we changed the Scala program‘s logic for displaying a board, so that instead of exploiting Doodle’s ability to automatically position images relative to each other, by combining them with the beside and above functions, the logic had to first explicitly position the images by itself, and then combine the images using the on function? We did that so that we could then translate the logic from Scala with Doodle to Haskell with Gloss. Imagine doing the equivalent in order to display the N-Queens solutions for N=4,5,6,7,8! While it could turn out to be relatively challenging, I don’t think that if we did do it, we would feel a great sense of accomplishment. While we might come across opportunities to use interesting functional programming techniques, I can imagine us being assailed by a growing sense that we are working on a fool’s errand. Let’s do something more interesting/constructive instead. In Part 4 we are going to first look at Haskell’s intersperse and intercalate functions, and then see an alternative way of solving the N-Queens problem, using the foldM function.
  • 27. I hope you enjoyed that. See you in Part 4. @philip_schwarz