Skip to content

Notes for Week 19 Zippers

newmana edited this page Apr 17, 2012 · 35 revisions

Links:

Exercises:

Question 1

From the example in the book:

let newFocus = modify (\_ -> 'P') (goRight (goLeft (freeTree,[])))  

What are the elements as in freeTree as it goes through the left and right branches and what element is replaced in the tree?

Question 2

Implement a text editor that:

  • Moves up a line,
  • Moves down a line,
  • Inserts a line,
  • Deletes a line,
  • Replaces a line,
  • Moves to the start of the document,
  • Moves to the end of the document.

Make sure we get can't go passed the top or bottom of the document.

Function signatures:

type HEdit = ([String], [String])
cursorUp :: HEdit -> HEdit
cursorDown :: HEdit -> HEdit
insertLine :: String -> HEdit -> HEdit
deleteLine :: HEdit -> HEdit
replaceLine :: String -> HEdit -> HEdit
startDocument :: HEdit -> HEdit
endDocument :: HEdit -> HEdit

Tests:

line1 = "first line"
line2 = "second line"
line3 = "third line"
emptyDocument = ([], [])
document = ([line1, line2, line3], [])
cursorUpOk1 = cursorUp(document) == document
cursorUpOk2 = cursorUp([line2, line3],[line1]) == document
cursorDownOk1 = cursorDown(document) == ([line2, line3], [line1])
cursorDownOk2 = cursorDown([], [line3, line2, line1]) == cursorDown([], [line3, line2, line1])
startDocumentOk = startDocument(cursorDown(cursorDown(document))) == document
endDocumentOk = endDocument(document) == ([], [line3, line2, line1])
insertLineOk = insertLine line1 (insertLine line2 (insertLine line3 emptyDocument)) == document
deleteLineOk1 = deleteLine document == ([line2, line3], [])
deleteLineOk2 = deleteLine emptyDocument == emptyDocument
deleteLineOk3 = deleteLine(endDocument(document)) == ([], [line2, line1])
replaceLineOk1 = replaceLine "skink" document == (["skink", line2, line3], [])
complicated = endDocument(replaceLine "george" (cursorUp(replaceLine "fred" (deleteLine(cursorDown(document)))))) == ([], ["fred", "george"])
allTests = [cursorUpOk1, cursorUpOk2, cursorDownOk1, cursorDownOk2, startDocumentOk, endDocumentOk, insertLineOk,
    deleteLineOk1, deleteLineOk2, deleteLineOk3, replaceLineOk1, complicated]

Question 3

A nexus is a tree that shares nodes (see page 3 of Functional Pearl Trouble Shared is Trouble Halved). It is used for memoisation - recording previously computed values. The example in the book is called "The Countdown Problem" which is like the numbers part of "Letters and Numbers". From the paper it gives a simple example, where it splits the value of the node into two parts using the following function:

isegs xs = [init xs, tail xs]

For example:

isegs [1,2,3,4] = [ [1,2,3],[2,3,4] ]

The inner nodes are shared, [2,3] is shared between tail [1,2,3] == init [2,3,4].

Write a program that simulates a nexus (I couldn't work out how to do a real one), using the following functions and data:

modify :: (a -> a) -> Zipper a -> Zipper a  
modify f (Node x l r, bs) = (Node (f x) l r, bs)  
modify f (Empty, bs) = (Empty, bs)

attach :: Tree a -> Zipper a -> Zipper a  
attach t (_, bs) = (t, bs)

isegs xs = [init xs, tail xs]
okIsegs = isegs [1,2,3,4] == [ [1,2,3],[2,3,4] ]

nexus :: Tree [Int]
startNexus = Node ([1,2,3,4]) Empty Empty
endNexus =
    Node [1,2,3,4]  
        (Node [1,2,3]  
            (Node [1,2]  
                (Node [1] Empty Empty)  
                (Node [2] Empty Empty)  
            )  
            (Node [2,3]
                (Node [2] Empty Empty)  
                (Node [3] Empty Empty)  
            )  
        )  
        (Node [2,3,4]  
            (Node [2,3]  
                (Node [2] Empty Empty)  
                (Node [3] Empty Empty)  
            )  
            (Node [3,4]  
                (Node [3] Empty Empty)  
                (Node [4] Empty Empty)  
            )  
        )

Make this True:

okBeginNexus = fst (createNexus (startNexus,[])) == (endNexus)

Here are some functions that should help:

getISegs :: (t, [Crumb [a]]) -> [a]
getISegs (_, LeftCrumb x r:bs) = x -: isegs -: head
getISegs (_, RightCrumb x l:bs) = x -: isegs -: last

okGo :: (Tree [a], t) -> Bool
okGo (Node x l r, bs) = length x > 2
okGo (Empty, _) = False

attachLeftSeg :: (Tree [a], Breadcrumbs' [a]) -> Zipper [a]
attachLeftSeg nexus = nexus -: goLeft' -: attach (Node (goLeft' nexus -: getISegs) Empty Empty)

attachRightSeg :: (Tree [a], Breadcrumbs' [a]) -> Zipper [a]
attachRightSeg nexus = nexus -: goRight' -: attach (Node (goRight' nexus -: getISegs) Empty Empty)

The functions to implement (use okGo in an if-then-else in createNexus):

attachSegs :: (Tree [a], Breadcrumbs' [a]) -> (Tree [a], Breadcrumbs' [a])

createNexus :: (Tree [a], Breadcrumbs' [a]) -> (Tree [a], Breadcrumbs' [a])

Questions raised during the group:

//TODO

Answers to Exercises:

Question 1

Starts at 'P', left to 'O', and right to 'Y' and replaces it with 'P'.

Question 2

import qualified Data.Monoid as M

type HEdit = ([String], [String])

cursorDown :: HEdit -> HEdit
cursorDown (x:xs, bs) = (xs, x:bs)  
cursorDown ([], bs) = ([], bs)

cursorUp :: HEdit -> HEdit
cursorUp (xs, b:bs) = (b:xs, bs)
cursorUp (xs, []) = (xs, [])

insertLine :: String -> HEdit -> HEdit
insertLine a (b, c) = (a:b, c)

deleteLine :: HEdit -> HEdit
deleteLine ([], []) = ([], [])
deleteLine ([], b:bs) = ([], bs)
deleteLine (x:xs, bs) = (xs, bs)

replaceLine :: String -> HEdit -> HEdit
replaceLine a (x:xs, b) = (a:xs, b)

startDocument :: HEdit -> HEdit
startDocument (xs, []) = (xs, [])
startDocument a = startDocument (cursorUp a)

endDocument :: HEdit -> HEdit
endDocument ([], bs) = ([], bs)
endDocument a = endDocument (cursorDown a)

line1 = "first line"
line2 = "second line"
line3 = "third line"
emptyDocument = ([], [])
document = ([line1, line2, line3], [])
cursorUpOk1 = cursorUp(document) == document
cursorUpOk2 = cursorUp([line2, line3],[line1]) == document
cursorDownOk1 = cursorDown(document) == ([line2, line3], [line1])
cursorDownOk2 = cursorDown([], [line3, line2, line1]) == cursorDown([], [line3, line2, line1])
startDocumentOk = startDocument(cursorDown(cursorDown(document))) == document
endDocumentOk = endDocument(document) == ([], [line3, line2, line1])
insertLineOk = insertLine line1 (insertLine line2 (insertLine line3 emptyDocument)) == document
deleteLineOk1 = deleteLine document == ([line2, line3], [])
deleteLineOk2 = deleteLine emptyDocument == emptyDocument
deleteLineOk3 = deleteLine(endDocument(document)) == ([], [line2, line1])
replaceLineOk1 = replaceLine "skink" document == (["skink", line2, line3], [])
complicated = endDocument(replaceLine "george" (cursorUp(replaceLine "fred" (deleteLine(cursorDown(document)))))) == ([], ["fred", "george"])
allTests = [cursorUpOk1, cursorUpOk2, cursorDownOk1, cursorDownOk2, startDocumentOk, endDocumentOk, insertLineOk,
    deleteLineOk1, deleteLineOk2, deleteLineOk3, replaceLineOk1, complicated]
testAll = (M.getAll $ M.mconcat $ map M.All allTests) == True

Question 3

data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Eq)  
data Crumb a = LeftCrumb a (Tree a) | RightCrumb a (Tree a) deriving (Show)  
type Breadcrumbs' a = [Crumb a]  
type Zipper a = (Tree a, Breadcrumbs' a)  

goLeft' :: (Tree a, Breadcrumbs' a) -> (Tree a, Breadcrumbs' a)  
goLeft' (Node x l r, bs) = (l, LeftCrumb x r:bs) 

goRight' :: (Tree a, Breadcrumbs' a) -> (Tree a, Breadcrumbs' a)  
goRight' (Node x l r, bs) = (r, RightCrumb x l:bs)  

goUp' :: (Tree a, Breadcrumbs' a) -> (Tree a, Breadcrumbs' a)  
goUp' (t, LeftCrumb x r:bs) = (Node x t r, bs)  
goUp' (t, RightCrumb x l:bs) = (Node x l t, bs)

attach :: Tree a -> Zipper a -> Zipper a  
attach t (_, bs) = (t, bs)

x -: f = f x  

isegs xs = [init xs, tail xs]
okIsegs = isegs [1,2,3,4] == [ [1,2,3],[2,3,4] ]

nexus :: Tree [Int]
nexus = Node ([1,2,3,4]) Empty Empty
endNexus =
    Node [1,2,3,4]  
    (Node [1,2,3]  
        (Node [1,2]  
            (Node [1] Empty Empty)  
            (Node [2] Empty Empty)  
        )  
        (Node [2,3]
            (Node [2] Empty Empty)  
            (Node [3] Empty Empty)  
        )  
    )  
    (Node [2,3,4]  
        (Node [2,3]  
            (Node [2] Empty Empty)  
            (Node [3] Empty Empty)  
        )  
        (Node [3,4]  
            (Node [3] Empty Empty)  
            (Node [4] Empty Empty)  
        )  
    )

getISegs :: (t, [Crumb [a]]) -> [a]
getISegs (_, LeftCrumb x r:bs) = x -: isegs -: head
-- Replace this function to use previously calculated result by going Up, Left and Right in the Zipper.
getISegs (_, RightCrumb x l:bs) = x -: isegs -: last

okGo :: (Tree [a], t) -> Bool
okGo (Node x l r, bs) = length x > 2
okGo (Empty, _) = False

attachLeftSeg :: (Tree [a], Breadcrumbs' [a]) -> Zipper [a]
attachLeftSeg nexus = nexus -: goLeft' -: attach (Node (goLeft' nexus -: getISegs) Empty Empty)

attachRightSeg :: (Tree [a], Breadcrumbs' [a]) -> Zipper [a]
attachRightSeg nexus = nexus -: goRight' -: attach (Node (goRight' nexus -: getISegs) Empty Empty)

attachSegs :: (Tree [a], Breadcrumbs' [a]) -> (Tree [a], Breadcrumbs' [a])
attachSegs nexus = nexus -: attachLeftSeg -: goUp' -: attachRightSeg -: goUp'

createNexus :: (Tree [a], Breadcrumbs' [a]) -> (Tree [a], Breadcrumbs' [a])
createNexus nexus = if (okGo nexus) then (attachSegs nexus -: goLeft' -: createNexus -: goUp' -: goRight' -: createNexus -: goUp') else (attachSegs nexus)

okBeginNexus = fst (createNexus (nexus,[])) == (endNexus)

Interesting Asides

10 Dimensions http://www.tenthdimension.com/medialinks.php
Clone this wiki locally