Skip to content

Manhattan Distance

Suppose we have a list of named coordinates:

scheme
(define points
 '((#\A 1 1)
   (#\B 1 6)
   (#\C 8 3)
   (#\D 3 4)
   (#\E 5 5)
   (#\F 8 9)))

They can be printed on a 2D grid (with dimensions according to the largest x and y coordinates, and (0, 0) on the top left)

.........
.A.......
.........
........C
...D.....
.....E...
.B.......
.........
.........
........F

Your task is to fill each . with the lower case letter of the closest coordinate according to the Manhattan distance. We call the resulting grid the nearest-neighbour grid.

aaaaa.ccc
aAaaa.ccc
aaaddeccc
aadddeccC
..dDdeecc
bb.deEeec
bBb.eeee.
bbb.eeeff
bbb.eefff
bbb.ffffF

The grid points which still contain a . represent tied points, i.e. points which have at least two equally close neighbours. The Manhattan distance between two points is defined by

Racket implementation

In Racket, write a function (grid points) that accepts a list of named points and computes the nearest neighbour grid. The output should be a list of strings where each string represents one row in the grid. Your file should be called manhattan.rkt

scheme
#lang racket
(provide grid)

(define (grid points)
    ; Implement me!
)

(define points
 '((#\A 1 1)
   (#\B 1 6)
   (#\C 8 3)
   (#\D 3 4)
   (#\E 5 5)
   (#\F 8 9)))

> (grid points)
'("aaaaa.ccc"
  "aAaaa.ccc"
  "aaaddeccc"
  "aadddeccC"
  "..dDdeecc"
  "bb.deEeec"
  "bBb.eeee."
  "bbb.eeeff"
  "bbb.eefff"
  "bbb.ffffF")

Hints

The following functions might be helpful.

To get lower case characters/strings: char-downcase and string-downcase.

scheme
> (char-downcase #\A)
#\a

To construct a string from a list of characters list->string.

scheme
> (list->string '(#\A #\B))
"AB"
Solution
scheme
#lang racket
(provide grid)

(define (absdiff x y) (abs (- x y)))
(define (manhattan x y) (apply + (map absdiff x y)))

(define/match (nearest-to-char group dist)
    (((list (cons a pt)) 0) a)
    (((list (cons a _)) _) (char-downcase a))
    (((cons _ _) _) #\.))

(define (get-symb pts loc)
  (define (keyfn pt) (manhattan loc (cdr pt)))
  (let ((groups (group-by keyfn (sort pts < #:key keyfn))))
    (nearest-to-char (car groups) (keyfn (caar groups)))))

(define (grid pts)
  (let ((w (apply max (map cadr pts)))
        (h (apply max (map caddr pts))))
    (for/list ((y (add1 h)))
      (for/list ((x (add1 w)))
        (get-symb pts (list x y))))))

Haskell Implementation

In Haskell, write a function grid :: [(Char,Int,Int)] -> [[Char]] that computes the nearest neighbour grid. The output should be a list of strings where each string represents one row in the grid. You file should be called Manhattan.hs, contain a module of the same name, and export the grid function:

haskell
module Manhattan (grid) where
import Data.Char
import Data.List

grid :: [(Char,Int,Int)] -> [[Char]]
-- grid = Implement me!

points :: [(Char, Int, Int)]
points = [
       ('A', 1, 1),
       ('B', 1, 6),
       ('C', 8, 3),
       ('D', 3, 4),
       ('E', 5, 5),
       ('F', 8, 9)]


> grid points
["aaaaa.ccc"
,"aAaaa.ccc"
,"aaaddeccc"
,"aadddeccC"
,"..dDdeecc"
,"bb.deEeec"
,"bBb.eeee."
,"bbb.eeeff"
,"bbb.eefff"
,"bbb.ffffF"]

Hints

The following functions might be helpful.

toLower from Data.Char to convert an upper case character to a lower case character

haskell
ghci> toLower 'A'
'a'

From the library Data.List you can use your favourite sorting function like sortBy or sortOn.

Solution
haskell
module Manhattan (grid) where
import Data.List
import Data.Char (toLower)

sortDists :: [(a, Int)] -> [(a, Int)]
sortDists = sortBy (\(_,x) (_,y) -> compare x y)

manhattan (x1,y1) (x2,y2) = abs (x1-x2) + abs (y1-y2)

dists points (x,y) = map (\(c,a,b) -> (c, manhattan (x,y) (a,b))) points

character ((cx,0):_) = cx
character ((cx,_):[]) = toLower cx
character ((cx,dx):(cy,dy):_) = if dx==dy then '.' else toLower cx

grid points = [[closest (i,j) | i <- [0..w]] | j<-[0..h]] where
  closest = character . sortDists . dists points
  w = maximum (map second points)
  h = maximum (map third points)

first (x,_,_) = x
second (_,x,_) = x
third (_,_,x) = x

points :: [(Char, Int, Int)]
points = [
       ('A', 1, 1),
       ('B', 1, 6),
       ('C', 8, 3),
       ('D', 3, 4),
       ('E', 5, 5),
       ('F', 8, 9)]

-- grid points
-- > ["aaaaa.ccc"
--   ,"aAaaa.ccc"
--   ,"aaaddeccc"
--   ,"aadddeccC"
--   ,"..dDdeecc"
--   ,"bb.deEeec"
--   ,"bBb.eeee."
--   ,"bbb.eeeff"
--   ,"bbb.eefff"
--   ,"bbb.ffffF"]