Real World Haskell - Exercise Answers - Chapter 3

Page 60 - Question 1
This took me a few minutes to get write - it was the first occasion where just reading the text is was insufficient for me to get the syntax right first time. (This is my problem, not a problem with the book.)
The answer to this question is slightly tricky because ':' is an infix operator, whereas the parallel Cons is prefix.

data List a = Cons a (List a)
            | Nil
              deriving (Show)
 
fromList (x:xs) = Cons x (fromList xs)
fromList []     = Nil
 
toList :: List a -> [a]
toList (Cons x (xs)) = (x:toList xs)
toList Nil           = []

Below is code using (:) as a prefix function, rather than it's normal role of an infix operator. This illustrates the perfect isomorphism between the new List type and the existing Haskell list type.
fromList2 ((:) x xs) = Cons x (fromList2 xs)
fromList2 []       = Nil
 
toList2 :: List a -> [a]
toList2 (Cons x xs) = (:) x (toList2 xs)
toList2 Nil       = []

Page 60 - Question 2
I'm not sure that I've got to the bottom of this question. My answer looks rather crude - suggestions for alternatives greatly appreciated.
data OCTree a = Node (Maybe a) (Maybe (OCTree a)) (Maybe (OCTree a))
                deriving (Show)
simpleTree = Node (Just "parent") (Just (Node (Just "left") Nothing Nothing)) (Just (Node (Just "right") Nothing Nothing))

Removing the option for the text to be optional shortens the answer slightly - but I still don't think I'm quite on the right track.
data OCTree2 a = Node2 a (Maybe (OCTree2 a)) (Maybe (OCTree2 a))
                 deriving (Show)
simpleTree2 = Node2 "parent" (Just (Node2 "left" Nothing Nothing)) (Just (Node2 "right" Nothing Nothing))

Page 69 - Questions 1 to 8
I found these questions to all be quite straightforward. The intersperse function has grown my confidence in using guards, as they show the functions behaviour so clearly.
module P69 () where
import Data.List
 
len :: [a] -> Integer
len [] = 0
len (x:xs) = 1 + len xs
 
mean :: [Double] -> Double
mean x = sum(x) / fromInteger(len x)
 
palindrome :: [a] -> [a]
palindrome [] = []
palindrome (x:xs) = x:(palindrome xs) ++ [x]
 
isPalindrome :: Eq a => [a] -> Bool
isPalindrome x = x == reverse x
 
sortByLength :: [[a]] -> [[a]]
sortByLength [] = []
sortByLength x = sortBy f x
    where
        f p q = compare (length p) (length q)
 
intersperse2 :: Char -> [String] -> String
intersperse2 _ [] = ""
intersperse2 c (x:[]) = x
intersperse2 c (x:xs) = x ++ [geshifilter-c] ++ intersperse2 c xs



Continue to <a href='http://www.finalcog.com/haskell-infinite-list-semi-primes'>right folds</a>... Continue to <a href='http://www.finalcog.com/real-world-haskell-exercise-answers-chapter-4'>chapter 4</a>...[/geshifilter-c]

Comments

v

旗袍是中国古老青花瓷瓶里开出的一朵郁金香,是上帝赐给中国女人的绝佳礼品;
旗袍美女,风姿卓越,贤淑典雅,娇媚迷人,配得上每一词的称赞;
唐装是时尚与经典的碰撞,是传统和现代的结合品,唐装汉服是时尚舞台永不褪色的主角;

唐思影-专业从事唐装批发旗袍批发,长期销售旗袍,唐装结婚旗袍新娘旗袍,男士唐装, 女士唐装, 儿童唐装唐装棉袄中式服装, 唐装汉服,新颖的款式,一流细致的做工,一款款唯美,古韵悠长的唐装、旗袍,畅销海内外:http://www.tsying.com.cn

Sweet teen girls

Sweet teen girls- Movies of a big tit blonde hottie gets fucked after a workout.
Teen lesbian orgasm video- Hot little teenies playing and kissing on the beach.
Young teen orgy groupsex-Three delightful cuties lustily lick tasty quims in kitchen.
Teen printed panty gallery- Everyday amateur girls acting li.
Teen hard movies-Movies of sultry babe sucks on a big dick and makes it cum on her face.

Questions 10-12

My solutions:

-- 10
data Direction = LeftTurn
               | RightTurn
               | StraightOn
                 deriving (Show, Eq)
 
-- 11
data Point = Point {
      pointX :: Double,
      pointY :: Double
} deriving (Show, Eq)
 
data Vector = Vector {
      startP :: Point,
      endP   :: Point
} deriving (Show, Eq)
 
getAngle :: Vector -> Double
getAngle v = atan2 y x
           where x = pointX (endP v) - pointX (startP v)
                 y = pointY (endP v) - pointY (startP v)
 
 
getDirection:: Point -> Point -> Point -> Direction
getDirection x y z | getAngle (Vector x y) < getAngle (Vector y z) = LeftTurn
                   | getAngle (Vector x y) > getAngle (Vector y z) = RightTurn
                   | otherwise = StraightOn
 
-- 12
getDirectionList :: [Point] -> [Direction]
getDirectionList xs | length xs < 3 = []
                    | otherwise = [getDirection a b c] ++ getDirectionList (tail xs)
                    where a = head xs
                          b = head (tail xs)
                          c = head (tail (tail xs))

Is atan2 cheating? I dunno, it's a fairly standard function in other languages, but it hadn't been covered in the book.

Page 60

toList :: List a -> [a]
toList Nil = []
toList (Cons a b) = a : toList b

-- Should return True after applying toList to fromList and getting the argument
checkList (x:xs) = toList (fromList (x:xs)) == (x:xs)

AND

-- Type: Tree a
-- A tree must have a node with an element, and a
-- left branch
-- right branch
-- Null branches are given with Nothing
data Tree a = Node a (Maybe (Tree a)) (Maybe (Tree a))
deriving (Show)
node = "node"
right = Just (Node "right" Nothing Nothing)
left = Just (Node "left" Nothing Nothing)

mytree = Node node (left) (right)

Cheers,

Mark Spezzano

could you reverse the order of the comments?

I noticed that the comments get posted with the most recent comment first. For instance, I posted my answers for p. 60 first and then my answers for p. 69-70. Ordering comments with the most recent comment first is pretty irritating. I can't think of any situation where reading comments backwards makes any sense.

Thanks.

intersperse guards?


The intersperse function has grown my confidence in using guards, as they show the functions behaviour so clearly.

intersperse2 _ [] = ""
intersperse2 c (x:[]) = x
intersperse2 c (x:xs) = x ++ [geshifilter-c] ++ intersperse2 c xs&#10;
Uhm...you didn't use any guards. Did you copy and paste the wrong version?[/geshifilter-c]

Page 69-70

1)

numItems [] = 0
numItems (x:xs) = 1 + numItems xs

In case anyone wants to compare how they are doing on the problems, it took me 3-4 hours to come up with that solution. So, about the only thing going for me at this point is my tenacity. I'm certainly not any good at Haskell.

2)

numItems :: [a] -> Int 

3)

myMean [] = error "list must have some elements"
myMean (x:[]) = x
myMean xs = (sum xs) / fromIntegral(length xs)

--improved error handling p.61
 
myMean2 [] = Nothing   
myMean2 (x:[]) =  Just x
 -- All the equations for a function must return 
 -- the same type! No, it was never explicitly 
 -- stated in the book.  The first equation 
 -- returns a Maybe type, so they all must. As far 
 -- as I can tell, when a function is defined as a 
 -- series of equations, each equation must have
 -- the same type of parameters, and each result
 -- must be the same type.
myMean2 xs =
    let len = fromIntegral(length xs)
    in let result = (sum xs)/len
        in Just result

4)

--list to palindrome 
 
l2p [] = []
l2p [x] = [x, x]
l2p xs =  xs ++ reverseIt xs
    where reverseIt [] = []
          reverseIt [x] = [x]   
          reverseIt xs = last xs : reverseIt(take (length xs - 1) xs)

It took me 3 hours to figure that one out. After struggling for 2 1/2 hours and getting nowhere, I decided to see if I could just write the reverseIt function. After coming up with reverseIt, I rediscovered the ++ operator, and that was the big breakthrough I needed for the final solution.

Thirteen exercises--ugh!

5)

isPali xs
    |xs == reverseIt xs     = True
    |otherwise              = False
   where reverseIt [] = []
         reverseIt [x] = [x]
         reverseIt xs = last xs : reverseIt(take (length xs - 1) xs)

That simple solution took me over an hour!

6)

import Data.List (sortBy)
 
mySort xs = sortBy myCompare xs
    where myCompare x y 
              | lx < ly    = LT
              | lx == ly   = EQ
              | lx > ly    = GT        
             where lx = length x
                   ly = length y

Two hours. I tried all kinds of ways to access the sortBy function in a program file with no success. I also knew that even if I could access sortBy, I wouldn't know how to call it. I checked sortBy's type in ghci by doing :module Data.List followed by :type sortBy, but the output was really confusing. That meant i had two problems to figure out. I searched in the Haskell docs for a sortBy example, but no luck. I ended up finding one elsewhere, and that showed me how to call sortBy. Then I looked around in chapter 4, and I found an example that used an import statement. However, an import statement didn't work in my code. By luck, I happened to move the code for this exercise into a separate file and it worked. My tests show that an import statement cannot be preceded by other lines of code. I had all my exercise solutions in one file, so my import statement was preceded by 100 lines of code. Arghh! I really hate it when a book asks you to solve an exercise using concepts that haven't been introduced yet. The authors get a demerit for this exercise.

7 & 8)

intersperse sep xs
    | null xs            = ""
    | length xs == 1     = head xs
    | length xs > 1      = head xs ++ sep ++ intersperse sep (tail xs)

9)

data Tree a = Node a (Tree a) (Tree a)
            | Empty
              deriving (Show)
 
height Empty = 0
height (Node _ Empty Empty)  = 1
height (Node _ node Empty) = 1 + height node  --go up left branch 
height (Node _ Empty node) = 1 + height node  --go up right branch
height (Node _ node1 node2)  --when first Node is connected to two Nodes
    | height node1 == height node2    = 1 + height node1
    | height node1 > height node2     = 1 + height node1
    | height node1 < height node2     = 1 + height node2

I got this one pretty easily. After getting the first 4 equations the last one didn't take too long. Plugging away on the simple cases seems to be a good approach. The thing that took me a long time was constructing Trees with enough twists and turns to test my solution properly. I found the easiest way to do that was to draw out a tree on paper, something like this:

                                          Empty   Empty
                                             \    /
                                              \  /
                                     Empty   Node 14
                                         \    /
                                          \  /
                                         Node 12  Empty
                                             \    /
                                              \  /
                                      Empty  Node 10
                                         \     /
                                          \   /
                  Empty   Empty   Node 7  Node 8
                      \    /          \     /
                       \  /            \   /
              Empty  Node 120   Empty  Node 6
                  \    /          \     /
                   \  /            \   /  
                 Node 100         Node 4
                      \             /
                       \           /
                        \         /
                         \       /
                          Node 2

Then I started at the top of the branches and constructed nodes like this:
*Main> let n120 = Node 120 Empty Empty
*Main> let n100 = Node 100 Empty n120
 
*Main> let n14 = Node 14 Empty Empty
*Main> let n12 = Node 12 Empty n14
*Main> let n10 = Node 10 n12 Empty
*Main> let n8 = Node 8 Empty n10
 
*Main> let n7 = Node 7 Empty Empty
*Main> let n5 = Node 5 Empty n7
 
*Main> let n6 = Node 6 n5 n8
*Main> let n4 = Node 4 Empty n6
*Main> let n2 = Node 2 n100 n4
*Main> n2
Node 2 (Node 100 Empty (Node 120 Empty Empty)) (Node 4 Empty (Node 6 (Node 5 Empty (Node 7 Empty Empty)) 
(Node 8 Empty (Node 10 (Node 12 Empty (Node 14 Empty Empty)) Empty))))
 
*Main> height n2
7

10)

data Direction = LeftTurn
               | RightTurn
               | Straight
                 deriving (Show)

11) Make sure your solution can handle this case:

*Main> whichWay (-2, 5) (4, 4) (5, -2)
RightTurn  --ok
*Main> whichWay (4,4) (-2, 5) (5, -2)
RightTurn  --wrong

whichWay (x1, y1) (x2, y2) (x3, y3)
 
{-In the formula for the slope of a line:
 
m = (y2 - y1)/(x2 - x1)
 
when the term (x2 - x1) is zero, you cannot use the slope intercept
equation for a line. When (x2 - x1) is equal to 0, the line drawn
from the 1st point to the 2nd point is a vertical line.
-}
 
--Cases where the line between the first two points is vertical:
    | x2 == x1 {-vertical line-} 
      && x3 == x2 {-3rd point is on line-}                  = Straight
 
      ---------------------
 
    | x2 == x1 {-vertical line-}
      && y2 < y1 {-2nd point is lower than 1st point-}
      && x3 < x2 {-3rd point is to left of line-}           = RightTurn
 
    | x2 == x1 {-vertical line-}
      && y2 < y1 {-2nd point is lower than 1st point-}
      && x3 > x2 {-3rd point is to right of line-}          = LeftTurn
 
     -----------------------
 
    | x2 == x1 {-vertical line-}
      && y2 > y1 {-2nd point is higher than 1st point-}
      && x3 < x2 {-3rd point is to left of line-}           = LeftTurn
 
    | x2 == x1 {-vertical line-}
      && y2 > y1 {-2nd point is higher than 1st point-}
      && x3 > x2 {-3rd points is to right of line-}         = RightTurn
 
--Cases where the slope intercept equation for a line can be used:
    | y3 == m*x3 + b {-3rd point is on line-}               = Straight
 
     ------------------------
 
    | y3 > m*x3 + b {-3rd point is above line-}
      && x2 < x1 {-2nd point is to left of 1st point-}      = RightTurn
 
    | y3 > m*x3 + b {-3rd point is above line-}
      && x2 > x1 {-2nd point is to right of 1st point-}     = LeftTurn
 
    | y3 < m*x3 + b {-3rd point is below line-}
      && x2 < x1 {-2nd point is to left of 1st point-}      = LeftTurn
 
    | y3 < m*x3 + b {-3rd point is above line-}
      && x2 > x1 {-2nd point is to right of 1st point-}     = RightTurn
 
   where m = (y2 - y1)/(x2 - x1)
         b = y - m*x
            where y = y2
                  x = x2

I needed to use block quotes for some of my comments, so I looked up what the symbols for block quotes are in haskell:
{- line1
line2
line3
line4
-}

12)

getDirections [] = error "list should contain at least 3 points"
getDirections (_:[]) = error "list should contain at least 3 points"
getDirections (_:_:[]) = error "list should contain at least 3 points"
getDirections (p1:p2:p3:[]) = [whichWay p1 p2 p3]
getDirections ps = let p1 = head ps
                       p2 = head (tail ps)
                       p3 = head (drop 2 ps)
                   in (whichWay p1 p2 p3) : getDirections (tail ps)

13) Problem 13 took me a week to figure out. I couldn't give up one problem shy of the finish!

First I looked at the convex hull web page. The diagram there made things clear what in the heck a convex hull was in the first place. Then I checked out the web page for the Graham scan. That was a little confusing. Being rusty on my trig, I looked up what a cotangent was. That didn't seem like much help. As far as I could tell, I needed to sort the points by the angle formed by a line drawn from P to each point. How do you figure out the angle each line segment forms with the x-axis? The Graham scan page suggests that you don't need to calculate any angles at all--you can use the cotangent instead. What the heck does calculating the cotangent tell you? For that matter, how do you even calculate a cotangent given two points. The definition that would prove useful was:

cotangent =  (adjacent side)/(opposite side)

Ok, so what does that mean? Here is an example:
                       . S (4, 5)
                      /|
                    /  |
                  /    | opp
                /      |
              .________|              
      (0, 0) P   adj

The length of the adjacent side is the difference between the x coordinates, and the length of the opposite side is the difference between the y coordinates. Therefore, the cotangent for the line segment PS is:
(4-0)/(5-0) = 4/5 

I couldn't readily understand how the cotangent would be able to identify the different angles formed by points in different quadrants, so to get a feel for what was going on, I decided to pick some points and calculate the cotangent of the line segment drawn from P to each point. I chose the origin (0, 0) for P. Here was the setup:
                      B
                      . (0, 3)
                      |             
            C         |         A  
   (-3, 2)  .         |         . (3, 2)
                      |      
----------------------.--------------
                      | P
   (-3, -2) .         |         . (3, -2)
            D         |         E
                      |

Note that when describing the angles formed by each of those points, you would say that PA forms an angle less than 90 degrees, PB forms a 90 degree angle, PC forms an angle greater than 90 degrees, PD forms an angle greater than 180 degrees(= 2 x 90), and PE forms an angle greater than 270 degrees(=3 x 90).

Here are the cotangents for the points A-E:

A : (3-0) / (2-0) = 3/2
 
B: As you move point A closer and closer to point B, the adjacent side gets smaller and smaller until 
it goes to zero.  So point B's adjacent side is 0, which makes the cotangent = 0/something, which is 0.
 
C: (-3-0) / (2-0) = -3/2      Ah hah!  The sign changes. :)
 
 
D: (-3-0) / (-2-0) = 3/2     Hmmm...That is the same answer as for A.  :(
 
E: (3-0) / (-2-0) = -3/2     Uggh.  Same answer as for C.

So the cotangent doesn't appear to be able to distinguish between some points that form bigger angles with the x axis than the angles formed by other points. But wait! P is going to be the lowest point. That means all the other points will be higher than P. That means all the points will be in the first two quadrants in relation to P. Therefore, the cotangent can distinguish between all those points. That is why the Graham scan page says simple arithmetic can help you determine which line segments form the biggest angles with the x axis.

Note that a point to the right of P and on the same horizontal line as P will have an infinite cotangent: the difference in y coordinates will be 0, which means you will be dividing by 0 to calculate the cotangent. Similarly, a point to the left of P and on the same horizontal line as P will have a cotangent that is -infinity. However, because of the way P is chosen, there can't be a point to the left of P that is on the same horizontal as P--otherwise that point would be P. So the cotangents run from +infinity to almost -infinity as the angles of the line segments change from 0-180 degrees. As a result, line segments that form a greater angle with the x axis will have smaller cotangents. Got it?

Ok, time to write the code to find P in a list of points, then write the code to sort the list of points by their cotangents with P.

{-
The following is a function that when given a list of pairs representing 
points finds the lowest point, i.e. the point with the smallest y coordinate.
In the case of ties, the point that lies furthest to the left, i.e. the one
with the smallest x coordinate is returned.
 
My strategy was to remove the first point in the list and compare it to
the last point in the list.  If the first point is a better P than
the last point, then the first point is appended to the end of the list.
Otherwise, the first point is discarded. Eventually, the list gets whittled 
down to one element, and that element is P.  
 
The efficiency will be O(2n) when every succeeding point in the list is a 
better candidate for P than the previous point because no points will be 
eliminated from the list until the list is 2*n long. But O(2n) is considered
to be O(n).  A for loop would be more efficient because it is always O(n).
-}
 
getP [] = error "empty list"
getP (p:[]) = p
getP (p:ps)
    | snd p < snd (last ps)         = getP (ps ++ [p])
      -- If y of first point in list is lower than y of last point in list,
      -- add the first point to end of list because it is a better P candidate.
 
    | snd p == snd (last ps)
      && fst p < fst (last ps)      = getP (ps ++ [p])
      -- If y of first point in list is the same height as y of last point in list,
      -- then check x coords.  If x of first point in list is further left than x of
      -- last point in list then add the first point to end of list because it is
      -- a better P candidate.
 
    | snd p == snd (last ps)
      && fst p > fst (last ps)      = getP ps
      -- If y of first point in list is the same height as y of last point in list,
      -- then check x coords.  If x of first point in list is to the right of the x of
      -- last point in list, then discard first point since it is not a better
      -- candidate for P than the last point in the list.
 
    | snd p > snd (last ps)         = getP ps
      --If y of first point in list is higher than y of last point in list, then
      --discard first point since it is cannot be P.

I had to keep adding to the following function as I discovered more and more different cases:

cotanSort p ps = sortBy myCompare ps
            where myCompare p1 p2
 
                --cotangent p->p1 = (p1x - px)/(p1y - py)
                --Calculating the cotangent for a few random points shows that the
                --bigger the cotangent, the smaller the angle that the line segment
                --forms with the x axis.  The sort should be from smallest angle 
                --to the biggest angle, which is the same as largest cotangent to
                --smallest cotangent.  Also, if the line segments p->p1 and p->p2 form the  
                --same angle with the x axis, i.e. their cotangents are the same, then the 
                --point closer to p should come earlier in the sorted list.
 
                    --Cases where p1 and p2 have the same cotangents:
                    | p1 == p2              = EQ
 
                    | p1y - py == 0  {-then cotangent of p->p1 is infinite-}
                      && p2y - py == 0 {-then cotangent of p->p2 is infinite-}
                      && p1x < p2x          = LT  {-p1 is closer to p-}
                    | p1y - py == 0  {-then cotangent of p->p1 is infinite-}
                      && p2y - py == 0 {-then cotangent of p->p2 is infinite-}
                      && p1x > p2x          = GT  {-p2 is closer to p-}
 
                    | cotanp1 == cotanp2
                      && cotanp1 > 0 {-then p1 and p2 are in 1st quadrant relative to p-}
                      && p1x < p2x          = LT {-then p1 is closer to p-}
                    | cotanp1 == cotanp2
                      && cotanp1 > 0 {-then p1 and p2 are in 1st quadrant relative to p-}
                      && p1x > p2x          = GT {-then p2 is closer to p-}
 
                    | cotanp1 == cotanp2
                      && cotanp1 == 0 {-then p1 and p2 are directly above p-}
                      && p1y < p2y          = LT {-then p1 is closer to p-}
                    | cotanp1 == cotanp2
                      && cotanp1 == 0 {-then p1 and p2 are directly above p-}
                      && p1y > p2y          = GT {-then p2 is closer to p-}
 
                    | cotanp1 == cotanp2
                      && cotanp1 < 0 {-then p1 and p2 are in 2nd quadrant relative to p-}
                      && p1x > p2x          = LT {-then p1 is closer to p-}
                    | cotanp1 == cotanp2
                      && cotanp1 < 0 {-then p1 and p2 are in 2nd quadrant relative to p-}
                      && p1x < p2x          = GT {-then p2 is closer to p-}
 
 
                    --Cases where p1 and p2 have different cotagents: 
                    | p1y - py == 0         = LT  --If gets here then p1 and p2 can't both
                                                  --have infinite cotangents.
                    | p2y - py == 0         = GT  --If gets here then only p2 can have an
                                                  --infinite cotangent.
                    | cotanp1 < cotanp2     = GT
                    | cotanp1 > cotanp2     = LT
 
                    where p1x = fst p1
                          p1y = snd p1
                          p2x = fst p2
                          p2y = snd p2
                          px = fst p
                          py = snd p
                          cotanp1 = (p1x - px)/(p1y - py)
                          cotanp2 = (p2x - px)/(p2y - py)

Now to write the getConvexHull function. I couldn't figure out how to use my getDirections function in the solution, so I didn't use it. I used my whichWay function instead, which returns a value such as RightTurn. But I quickly discovered that you can't do something like this:

if whichWay p1 p2 p3 == RightTurn
then 1
else 2

That produces an error like:
   No instance for (Eq Direction)
      arising from a use of `==' at <interactive>:1:3-14
    Possible fix: add an instance declaration for (Eq Direction)
    In the predicate expression: dir == RightTurn
    In the expression: if dir == RightTurn then 1 else 2
    In the definition of `it': it = if dir == RightTurn then 1 else 2

I solved that problem with a case expression:
case whichWay p1 p2 p3 of
       RightTurn   -> <do something>
       _           -> <do something else>

It seems like you would also be doing an == comparison in a case expression when you pattern match against the result of whichWay, but apparently not. (Edit: I happened to be rereading p. 48, and the example there shows how you can activate the == operator for comparisons of data types.)

Here was the result of my initial attempt to write getConvexHull

getConvexHull [] = error "there must be at least on point in the list"
getConvexHull (p1:[]) = [p1]
getConvexHull (p1:p2:[]) = [p1, p2]
getConvexHull (p1:p2:p3:[]) = [p1, p2, p3] 
getConvexHull ps =
        let p = getP ps  
            orderedPs = cotanSort p ps 
            p1 = head orderedPs
            p2 = last (take 2 orderedPs)
            p3 = last (take 3 orderedPs)
         in case whichWay p1 p2 p3 of
             RightTurn   -> getConvexHull (head orderedPs : drop 2 orderedPs)
             _           -> head orderedPs : getConvexHull (tail orderedPs)

That compiled without error, but the results it produced weren't any good. The problem was every time the code recursively called getConvexHull the function was recalculating p and also resorting the list. Resorting the list didn't necessarily produce errors--but it was obviously inefficient to sort the list multiple times. The real problem was that p was at the front of orderedPs, and when it got chopped off the front of orderedPs by the call:
getConvexHull(tail orderedPs)

as the function moved on to consider the next group of three points, the first thing the recursive function call did was to pick a new p from the remaining points:
p = getP ps

which is NOT what you want to do. p doesn't change.

Somehow I needed to recursively call getConvexHull without the function recalculating p or resorting the remaining list. In essence, I needed to figure out how to skip these two statements:

p = getP ps  
orderedPs = cotanSort p ps 

for all subsequent calls to getConvexHull. Somehow I needed to write a function identical to getConvexHull but without those two lines, and call the identical function from inside getConvexHull. That got me playing around with nested functions. I found it invaluable to have another file open to test small snippets of code I was contemplating on adding to my growing main program. Here are a few of the things I messed around with:
myfunc xs = let a = 2
                b = 3   
                innerFunc _ = xs   --returns the myfunc argument for any call of innerFunc
            in case innerFunc "hello" of   --calls innerFunc and then examines the result
                (x:[])  -> a + b + c 
                _       -> 0    
        where c = 4
 
 
--output:--
*Main> myfunc [3]
9

data A = MyLeft
       | MyRight 
         deriving (Show)
 
myfunc2 xs = innerFunc xs    --call innerFunc with xs as the arg
        where innerFunc (p:ps) =
                case p of
                    MyRight -> 1
                    MyLeft  -> 0
 
--output:--
*Main> myfunc [MyRight]
1

Ok, back to the main program. Here's what I came up with for my final solution for getConvexHull:
getConvexHull [] = error "there must be at least one point in the list"
getConvexHull (p1:[]) = [p1]
getConvexHull (p1:p2:[]) = [p1, p2]        
getConvexHull ps = helperFunc orderedPs    --call helperFunc
            where p = getP ps  
                  orderedPs = cotanSort p ps   --define orderedPs in the first line
 
                  helperFunc (p1:p2:[]) = [p1, p2]   --define helperFunc in the first line
                  helperFunc xs =
                        let p1 = head xs
                            p2 = last (take 2 xs)
                            p3 = last (take 3 xs)
                        in case whichWay p1 p2 p3 of
                                RightTurn   -> helperFunc (head xs : drop 2 xs)  --eliminate 2nd point from list and try again
                                _           -> head xs : helperFunc (tail xs)  --the first point is on the convex hull so store it 
                                                                               --in a list and get the rest of the convex hull

It seems to work:
1) A simple case:
 
*Main> getP [(2, 2), (0,0), (1,0), (3,3)]
(0,0)
 
*Main> cotanSort (0, 0) [(2, 2), (0,0), (1,0), (3,3)]
[(0.0,0.0),(1.0,0.0),(2.0,2.0),(3.0,3.0)]
 
*Main> getConvexHull [(2, 2), (0,0), (1,0), (3,3)]
[(0.0,0.0),(1.0,0.0),(3.0,3.0)]
 
 
 
2) Points with lots of twists and turns:
*Main> getP [(0,5), (3,3), (2, 2), (-5, 1), (0, 3), (2, 3), (0, 0), (1, 0), (2, 5)]
(0,0)
 
*Main> cotanSort (0, 0) [(0,5), (3,3), (2, 2), (-5, 1), (0, 3), (2, 3), (0, 0), (1, 0), (2, 5)]
[(0.0,0.0),(1.0,0.0),(2.0,2.0),(3.0,3.0),(2.0,3.0),(2.0,5.0),(0.0,3.0),(0.0,5.0),(-5.0,1.0)]
 
*Main> getConvexHull [(0,5), (3,3), (2, 2), (-5, 1), (0, 3), (2, 3), (0, 0), (1, 0), (2, 5)]
[(0.0,0.0),(1.0,0.0),(3.0,3.0),(2.0,5.0),(0.0,5.0),(-5.0,1.0)]

I displayed the chosen P and the list of sorted points so that I could draw the line segments by hand, and then eliminate a previous point that caused me to make a right turn. That allowed me to verify the result produced by getConvexHull.

**Edit:***
I read a comment related to someone else's solution, and the comment said, "your solution can't be correct because it doesn't backtrack". I knew my solution didn't backtrack either. When my function encounters a right turn it deletes the 2nd point and summons the next point in the list, always moving forward in the list. I didn't think backtracking was necessary, but the poster gave an example that demonstrates it is:

[(-3,7),(-2,6),(-1,4),(0,1),(0,0),(1,4),(2,6),(3,7)]

The convex hull of those points is the triangle: [(0.0,0.0),(3.0,7.0),(-3.0,7.0)], which is not the answer my solution produces. When you encounter multiple right turns in a row, backtracking is necessary. So I spent another day fixing my function. Backtracking introduces some challenges. Here is my improved getConvexHull function:

getConvexHull [] = error "there must be at least one point in the list"
getConvexHull (p1:[]) = [p1]
getConvexHull (p1:p2:[]) = [p1, p2]        
getConvexHull ps = helperFunc [] orderedPs
    where p = getP ps --defines p used in next line 
          orderedPs = cotanSort p ps --defines orderedPs in first line
 
          --defines helperFunc in first line:
          helperFunc hullPoints (p1:p2:[]) = hullPoints ++ [p1, p2] --Ending condition.  If there are only two points left,
                                                                    --they are both on the convex hull.
          helperFunc hullPoints remainingPoints =                 
              let p1 = head remainingPoints
                  p2 = last (take 2 remainingPoints)
                  p3 = last (take 3 remainingPoints)
              in case whichWay p1 p2 p3 of
                        RightTurn   -> helperFunc hullPointsMinusLast lastConsRemainingPoints 
                        _           -> helperFunc (hullPoints ++ [p1]) (tail remainingPoints)
 
                  where hullPointsMinusLast = if length hullPoints == 0 || length hullPoints == 1
                                              then [] 
                                              else take (length hullPoints - 1) hullPoints
                        remainingPointsMinus2nd = head remainingPoints : drop 2 remainingPoints   
                        lastConsRemainingPoints = last hullPoints : remainingPointsMinus2nd

I was really having a lot of problems related to the recursive case for LeftTurns and Straight. I was initially trying to build up a result list using something like:
hullPoints ++ recursiveFunc

but I was getting bad results, and I couldn't figure out why. I finally realized that the two lists: hullPoints and remainingPoints, have to grow and shrink in tandem. The potential for backtracking means that you can't build a result list as you go along--because you can't be sure the current hullPoints will all be in the final result. In that line of code above, the list hullPoints is never going to get any shorter because it is outside the function call so it can't be altered by the function call--except to make it longer.

I finally came up with the idea to remove hullPoints from the left side of the expression above and let the ending condition:

helperFunc hullPoints (p1:p2:[]) =

return the result:
helperFunc hullPoints (p1:p2:[]) = hullPoints ++ [p1, p2]

The recursive cases just change the value of the parameter variable hullPoints by calling the function with a new argument for hullPoints--until the ending condition is reached, and then the final value of the parameter variable hullPoints is returned.

I have been reading ahead in chapter 4, and the authors illustrate similar tricks where you use one of the function's parameter variables to accumulate the result.

Problem 13: Graham Scan Convex Hull Solution

-- A direction is either a left turn, a right turn or straight ahead
data Direction = LeftH | RightH | Straight
deriving (Show, Eq)

-- A two-dimensional (x,y) coordinate
data Point = Point {
x :: Double,
y :: Double
} deriving (Show)

type Cotangent = Double

-- Make Point have equality
instance Eq Point where
p1 == p2 = (x p1 == x p2) && (y p1 == y p2)

-- Make Point be comapable (for sorting)
instance Ord Point where
compare p1 p2
| (x p1 == x p2) && (y p1 == y p2) = EQ
| (y p1 == y p2) && (x p1 < x p2) = LT
| (y p1 < y p2) = LT
| otherwise = GT

-- Function: determinant
-- Calculate the 3x3 determinant:
-- | x1 y1 1 |
-- | x2 y2 1 |
-- | x3 y3 1 |
-- First argument is point a
-- Second argument is point b
-- Third argument is point c
determinant :: Point -> Point -> Point -> Double
determinant a b c = (x b - x a) * (y c - y a) -
(y b - y a) * (x c - x a)

-- Function getDirection
-- Returns the Direction based on the sign of the determinant of the Point
-- First argument is point a
-- Second argument is point b
-- Third argument is point c
getDirection :: Point -> Point -> Point -> Direction
getDirection a b c
| (determinant a b c) > 0 = LeftH
| (determinant a b c) < 0 = RightH
| otherwise = Straight

-- Function: getDirections
-- Returns the list of Directions based on the list of Points
-- Argument is the list of points
getDirections :: [Point] -> [Direction]
getDirections (_:[]) = error "Must supply at least three vertices"
getDirections (_:_:[]) = error "Must supply at least three vertices"
getDirections (a:b:c:[]) = [getDirection a b c]
getDirections (a:b:c:xs) = (getDirection a b c) : getDirections (b:c:xs)

-- Function: sortPoints
-- Returns the points in sorted order, with the pivot at the head
-- Argument is the list of unsorted points
sortPoints :: [Point] -> [Point]
sortPoints ps = sort ps

-- Function: getContangents
-- Returns the cotangents of the sorted list relative to the pivot
-- Argument is the list of sorted Points with the pivot at the head
getCotangents :: [Point] -> [Cotangent]
getCotangents (_:[]) = []
getCotangents (p1:p2:ps) = cot : getCotangents (p1:ps)
where cot = (x p2 - x p1) / (y p2 - y p1)

-- Function: zipCotangents
-- Returns the Points tupled with the Cotangents
-- Argument is the list of raw points
zipCotangents :: [Point] -> [(Point, Cotangent)]
zipCotangents ps = zip (tail sorted) (getCotangents sorted)
where sorted = sortPoints ps

-- Function: sortByCotangent
-- Returns the Points in cotangent order
-- Argument is the list of unsorted Point-Cotangent tuples
sortByCotangent :: [(Point, Cotangent)] -> [Point]
sortByCotangent (p:ps) = fst (unzip (reverse (sortBy (compareCotangents) (p:ps))))
where
compareCotangents p1 p2
| (snd p1 < snd p2) = LT
| (snd p1 == snd p2) = EQ
| otherwise = GT

-- Function: startPoints
-- Returns the first two points of the processed Point list
-- Argument is the raw Point list
startPoints :: [Point] -> [Point]
startPoints ps = elem2 : [elem1]
where
elem1 = head (sortPoints ps)
elem2 = head (sortByCotangent (zipCotangents ps))

-- Function: grahamMain
-- Returns the convex hull of the points using the Graham scan
-- First argument is the list of Points
-- Second argument is the empty list, and holds a running list of valid points
grahamMain :: [Point] -> [Point] -> [Point]
grahamMain (p:ps) [] = grahamMain ps (startPoints (p:ps))
grahamMain (p:ps) (x2:x1:xs)
| (getDirection x1 x2 p) == Straight = grahamMain ps (p:x2:x1:xs)
| (getDirection x1 x2 p) == LeftH = grahamMain ps (p:x2:x1:xs)
| otherwise = grahamMain ps (p:x1:xs)
grahamMain [] xs = reverse xs

-- Function: checkGrahman
-- Returns true if the Graham function recgonises the first object as the second
-- e.g. checkGraham thingy octagon
-- First argument is the set of dot to check
-- Second argument is the convex hull of those dots
checkGraham :: [Point] -> [Point] -> Bool
checkGraham p1 p2 = (grahamMain p1 []) == (grahamMain p2 [])

-- Sample shapes

-- Convex hull
octagon :: [Point]
octagon = [Point 6 1, Point 8 3, Point 8 5, Point 6 7,
Point 4 7, Point 2 5, Point 2 3, Point 4 1]

-- Non-convex thing
thingy :: [Point]
thingy = [Point 6 1, Point 6.5 3, Point 8 3, Point 8 5, Point 6 7,
Point 4 7, Point 2 5, Point 3.5 4, Point 2 3, Point 4 1]

If anyone can think of ways to improve this, please let us know.

Cheers

Mark

page 60

1) The following is the same data type definition as on p.58. I changed the name because I kept thinking the List type was the Haskell built-in list type.

data MyList a = Cons a (MyList a)
              | Nil
                deriving(Show)

Here is the original function provided by the book to convert a Haskell built-in list to a MyList (top of p.59):
haskToMyList (x:xs) = Cons x (haskToMyList xs)
haskToMyList [] = Nil

Example of a MyList type created with the Cons and the Nil value constructors above(p. 59):
Cons "d" (Cons "u" (Cons "r" (Cons "i" (Cons "a" (Cons "n" Nil)))))

Now we need to convert that to a haskell list. A function can be defined as a series of equations(p.50). Here is my solution:
myListToHask (Cons first theRest) = first:(myListToHask theRest)
myListToHask Nil = [] 

--sample output:--
 
*Main> :load hask1.hs 
[1 of 1] Compiling Main             ( hask1.hs, interpreted )
Ok, modules loaded: Main.
*Main> let my_list = haskToMyList [10, 20, 30]
*Main> my_list
Cons 10 (Cons 20 (Cons 30 Nil))
*Main> myListToHask my_list
[10,20,30]
*Main> let my_list = haskToMyList "hello"
*Main> my_list
Cons 'h' (Cons 'e' (Cons 'l' (Cons 'l' (Cons 'o' Nil))))
*Main> myListToHask my_list
"hello"
*Main> 

2)

data Tree a = Node a (Maybe (Tree a)) (Maybe (Tree a))
            deriving(Show)

The Node value constructor here:
Node a (Maybe (Tree a)) (Maybe (Tree a))

takes three arguments, any type, a Maybe type, and a Maybe type:

a, Maybe (Tree a), Maybe (Tree a)

Maybe is a polymorphic type and in this case its type parameter is specified as "Tree a". Maybe has a value constructor named Just, and the Node value constructor above produces values of type "Tree a". So you can use a term like:
Just(Node <args>)

as an argument to match the term Maybe(Tree a). For example:
Just(Node 12 Nothing Nothing)

Note that Nothing is also a Maybe value constructor--one that takes no arguments. The "value" it produces is the name of the value constructor with nothing after it. Compare that "value" to the value produced by the Book value constructor on p. 42:
ghci> Book 0 "The Book of Imaginary Beings" ["Jorge Luis Borges"]
Book 0 "The Book of Imaginary Beings" ["Jorge Luis Borges"]

So it appears that in Haskell a "value" is just the name of the value constructor followed by the specified arguments. Initially, I was somewhat confused by the Nothing value constructor for a Maybe type--because, well, it didn't produce any value. But it follows the same pattern as the Book value constructor: the value that it produces is just the constructor name followed by any args. Because the Nothing value constructor takes no args, the value it produces is just the name of the value constructor.
--sample output:--
*Main> let left_leaf = Node 12 Nothing Nothing
*Main> let right_leaf = Node 14 Nothing Nothing
*Main> let left_tree = Just(left_leaf)
*Main> let right_tree = Just(right_leaf)
*Main> let my_tree = Node 12 left_tree right_tree
*Main> my_tree
Node 12 (Just (Node 12 Nothing Nothing)) (Just (Node 14 Nothing Nothing))
*Main> 

Problem 9--one line of code

Problem 9:

-- Type: Tree a
-- A tree must have a Node with an element, and a
-- left branch and a right branch or be Empty
data Tree a = Node a (Tree a) (Tree a) | Empty
deriving (Show)

-- Sample tree
-- a
-- / \
-- b g
-- / \ / \
-- c f h k
-- / \ | \
-- d e i j
-- |
-- L
-- Height = 5 = (a,g,h,j,L)
tree :: Tree Char
tree = Node 'a' left right
where left = Node 'b' (Node 'c' (Node 'd' Empty Empty) (Node 'e' Empty Empty)) (Node 'f' Empty Empty)
right = Node 'g' (Node 'h' (Node 'i' Empty Empty) (Node 'j' (Node 'L' Empty Empty) Empty)) (Node 'k' Empty Empty)

-- Function: height
-- Returns the height of a Tree
-- Argument is the Tree to measure
height :: Tree a -> Int
height Empty = 0
height (Node a lft rgt) = 1 + (max (height lft) (height rgt))

Here the height is 1 plus the height of either the left subtree or the right subtree, so we take the maximum.

One line! :)

Cheers,

Mark Spezzano

My Solutions to Problems 1--8

By the way, 7Stud, I noticed that you're taking a long time so solve these problems. To compare, I've solved these first 8 problems in 1.5 hours total. That doesn't make me good at Haskell (I'm not) but I have learned (some) Haskell from different books and video lectures on the internet. I also learned Miranda (almost the same) at Uni so that helps. Eventually you get used to thinking the functional way. Things just click. But it takes time and practice, practice, practice. Recursive function calls are the hardest to master I think.

I HIGHLY (highly) recommend the series of lectures by Jurgen Geist: http://www.haskell.org/haskellwiki/Tutorials
and download the LECTURE movies (not the tutorials).

Sit in the lecture, get out a notebook and take careful notes. It's amazing how much you can learn from his explanations. And completely free. But make sure you take written notes of everything. It really helps the material to sink in.

Here goes

Problem 1

-- Function: getLength
-- Returns the length of a list
-- Argument is the list to measure
getLength :: [a] -> Int
getLength [] = 0
getLength (x:xs) = 1 + getLength xs

Problem 2

-- Function: checkLength
-- Returns whether the length returned by getLength is the same as the length
-- returned by length
-- Argument is the list to check
checkLength :: [a] -> Bool
checkLength xs = getLength xs == length xs

Problem 3
-- Function: meanList
-- Returns the mean (average) of all values in the list
-- Argument is the list of floating-point numbers to average
meanList :: [Double] -> Double
meanList [] = 0
meanList xs = (sum xs) / fromIntegral (length xs)

Problem 4
-- Function: palindrome
-- Returns the palindrome of a list
-- Argument is the list to palindrome
palindrome :: [Int] -> [Int]
palindrome [] = []
palindrome xs = xs ++ reverse (xs)

Problem 5
-- Function: isPalindrome
-- Returns True if the supplied argument is a palindrome
-- Argument is the list to check
isPalindrome :: [Int] -> Bool
isPalindrome [] = True
isPalindrome (xs) = xs == reverse (xs)

Problem 6:
-- Function: sortLists
-- Returns a list of lists sorted by the length of each sublist
-- Arugment is a list of lists
sortLists :: [[a]] -> [[a]]
sortLists [] = []
sortLists (x:xs) = sortBy compareLength (x:xs)
where compareLength as bs
| length (as) < length (bs) = LT
| length (as) == length (bs) = EQ
| otherwise = GT

Problem 7 & 8:
-- Function: intersperse
-- Returns a list with a separator between each sublist
-- First argument: separator value
-- Second argument: list of lists to separate
intersperse :: a -> [[a]] -> [a]
intersperse a [] = []
intersperse a (x:[]) = x
intersperse a (x:xs) = x ++ [a] ++ (intersperse a xs)

NOTE: it's always a good idea to write comments before the argument signature.

Cheers,

Mark Spezzano

Solutions to Problems 10,11,12

Problem 10:

-- A direction is either a left turn, a right turn or straight ahead
data Direction = LeftH | RightH | Straight
deriving (Show)

Problem 11:

-- A two-dimensional (x,y) coordinate
type Point = (Double, Double)

-- Function: determinant
-- Calculate the 3x3 determinant:
-- | x1 y1 1 |
-- | x2 y2 1 |
-- | x3 y3 1 |
-- First argument is point a
-- Second argument is point b
-- Third argument is point c
determinant :: Point -> Point -> Point -> Double
determinant a b c = (fst b - fst a) * (snd c - snd a) -
(snd b - snd a) * (fst c - fst a)

-- Function getDirection
-- Returns the Direction based on the sign of the determinant of the Point
-- First argument is point a
-- Second argument is point b
-- Third argument is point c
getDirection :: Point -> Point -> Point -> Direction
getDirection a b c
| det > 0 = LeftH
| det < 0 = RightH
| otherwise = Straight
where det = determinant a b c

Problem 12:

-- Function: getDirections
-- Returns the list of Directions based on the list of Points
-- Argument is the list of points
getDirections :: [Point] -> [Direction]
getDirections (a:b:c:[]) = [getDirection a b c]
getDirections (a:b:c:xs) = (getDirection a b c) : getDirections (b:c:xs)

-- Sample shape
octagon :: [Point]
octagon = [(4,1), (6,1), (8,3), (8,5), (6,7), (4,7), (2,5), (2,3)]

Note that a) it can be a bad idea to calculate the slope of the line if the line segment is vertical (slope is undefined)
b) you could add in an error-checking line or two for getDirections in case less than three arguments are specified.

Mark Spezzano