Rotating Squares
04 Jan 2017Here’s a short one.
I use Colin Percival’s Hacker News Daily to catch the top ten articles of the day on Hacker News. Today an article called Why Recursive Data Structures? popped up, which illustrates that recursive algorithms can become both intuitive and borderline trivial when a suitable data structure is used to implement them. This is exactly the motivation for using recursion schemes.
In the above article, Reginald rotates squares by representing them via a quadtree. If we have a square of bits, something like:
.x..
..x.
xxx.
....
then we want to be able to easily rotate it 90 degrees clockwise, for example. So let’s define a quadtree in Haskell:
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE LambdaCase #-}
import Data.Functor.Foldable
import Data.List.Split
data QuadTreeF a r =
NodeF r r r r
| LeafF a
| EmptyF
deriving (Show, Functor)
type QuadTree a = Fix (QuadTreeF a)
The four fields of the ‘NodeF’ constructor correspond to the upper left, upper right, lower right, and lower left quadrants of the tree respectively.
Gimme some embedded language terms:
node :: QuadTree a -> QuadTree a -> QuadTree a -> QuadTree a -> QuadTree a
node ul ur lr ll = Fix (NodeF ul ur lr ll)
leaf :: a -> QuadTree a
leaf = Fix . LeafF
empty :: QuadTree a
empty = Fix EmptyF
That lets us define quadtrees easily. Here’s the tree that the previous diagram corresponds to:
tree :: QuadTree Bool
tree = node ul ur lr ll where
ul = node (leaf False) (leaf True) (leaf False) (leaf False)
ur = node (leaf False) (leaf False) (leaf False) (leaf True)
lr = node (leaf True) (leaf False) (leaf False) (leaf False)
ll = node (leaf True) (leaf True) (leaf False) (leaf False)
Rotating is then really easy - we rotate each quadrant recursively. Just reach for a catamorphism:
rotate :: QuadTree a -> QuadTree a
rotate = cata $ \case
NodeF ul ur lr ll -> node ll ul ur lr
LeafF a -> leaf a
EmptyF -> empty
Notice that you just have to shift each field of ‘NodeF’ rightward, with wraparound. Then if you rotate and render the original tree you get:
.x..
.x.x
.xx.
....
Rotating things more times yields predictable results.
If you want to rotate another structure - say, a flat list - you can go through a quadtree as an intermediate representation using the same pattern I described in Sorting with Style. Build yourself a coalgebra and algebra pair:
builder :: [a] -> QuadTreeF a [a]
builder = \case
[] -> EmptyF
[x] -> LeafF x
xs -> NodeF a b c d where
[a, b, c, d] = chunksOf (length xs `div` 4) xs
consumer :: QuadTreeF a [a] -> [a]
consumer = \case
EmptyF -> []
LeafF a -> [a]
NodeF ul ur lr ll -> concat [ll, ul, ur, lr]
and then glue them together with a hylomorphism:
rotateList :: [a] -> [a]
rotateList = hylo consumer builder
Neato.
For a recent recursion scheme resource I’ve spotted on the Twitters, check out Pascal Hartig’s compendium in progress.