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 = []fromList2 ((:) x xs) = Cons x (fromList2 xs) fromList2 [] = Nil toList2 :: List a -> [a] toList2 (Cons x xs) = (:) x (toList2 xs) toList2 Nil = []
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))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))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
Comments
ag
Louis Vuitton Genuine Leather Mahina Pinky Cruise Handbag 95515 180.80
Louis Vuitton Embossed Leather Embossed Leather Handbag M1013 Bl 199.20
Louis Vuitton Genuine Leather Latest Fashion Shoulder Bag 6258 B 151.20
Louis Vuitton Genuine Leather Utah Leather Kiowa Tote 95817 151.20
Louis Vuitton Embossed Leather Embossed Leather Handbag M1013 Ye 199.20
Louis Vuitton Embossed Leather Embossed Leather Handbag M1013 Of 199.20
Louis Vuitton Monogram Leather RABBIT HAIR M95589 Purple 158.40
Louis Vuitton Embossed Leather Embossed Leather Handbag M1013 Bl 199.20
Balenciaga Genuine Leather Cutout Detail Medium Bag 2949 Silver 151.20
Balenciaga Genuine Leather Cutout Detail Medium Bag 2949 Purple 151.20
Balenciaga Genuine Leather Cutout Detail Medium Bag 2949 White 151.20
Balenciaga Genuine Leather Midday Bag 7746 158.40
Balenciaga Genuine Leather Horsehair Leather Handbags 084385 175.20
Balenciaga Lambskin Leather Hook Bag 7732 148.00
Louis Vuitton Monogram Canvas Globe Shopper Cabas Pm M95116 Off- 149.60
Louis Vuitton Monogram Canvas Globe Shopper Cabas Pm M95116 Gray 149.60
Balenciaga Lambskin Leather Hook Bag 820 148.00
Balenciaga Genuine Leather Cutout Detail Medium Bag 2949 Pink 151.20
Balenciaga Genuine Leather Cutout Detail Medium Bag 2949 151.20
Balenciaga Genuine Leather Cutout Detail Medium Bag 2949 White 151.20
Balenciaga Genuine Leather Midday Bag 7746 158.40
v
旗袍是中国古老青花瓷瓶里开出的一朵郁金香,是上帝赐给中国女人的绝佳礼品;
旗袍美女,风姿卓越,贤淑典雅,娇媚迷人,配得上每一词的称赞;
唐装是时尚与经典的碰撞,是传统和现代的结合品,唐装汉服是时尚舞台永不褪色的主角;
唐思影-专业从事唐装批发,旗袍批发,长期销售旗袍,唐装,结婚旗袍,新娘旗袍,男士唐装, 女士唐装, 儿童唐装,唐装棉袄,中式服装, 唐装汉服,新颖的款式,一流细致的做工,一款款唯美,古韵悠长的唐装、旗袍,畅销海内外:http://www.tsying.com.cn
Teen lesbian pussy sucking
Teen lesbian pussy sucking
Teen ebony pussy licking
Hardcore teen video clip
Latina teen anal sex
Teen creampie sex clips
Chubby girl hose in pantie
Chubby girl hose in pantie
Babe big boob fucked sexy
Free naked black foot pics
Amateur teen pussy pictures
Pretty girl with big boob
Hot blondie ally gets
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.
Page 69-70
1)
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)
3)
--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 result4)
--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 yTwo 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 node2I 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 2Then I started at the top of the branches and constructed nodes like this:
10)
data Direction = LeftTurn | RightTurn | Straight deriving (Show)11) Make sure your solution can handle this case:
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 = x2I 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:
Ok, so what does that mean? Here is an example:
. S (4, 5) /| / | / | opp / | .________| (0, 0) P adjThe 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:
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:
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:
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 2I 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:
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:
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:
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] 9data 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] 1Ok, 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 hullIt seems to work:
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 : remainingPointsMinus2ndI 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:
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:
return the result:
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):
Example of a MyList type created with the Cons and the Nil value constructors above(p. 59):
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:
2)
data Tree a = Node a (Maybe (Tree a)) (Maybe (Tree a)) deriving(Show)The Node value constructor here:
takes three arguments, any type, a Maybe type, and a Maybe type:
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:
as an argument to match the term Maybe(Tree a). For example:
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:
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.
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