It looks like this is a really classical question, but I'd like to ask it one more time as all those solutions look quite long and complicated for me (maybe because I am dumb :) )
So this is a Graham convex hull algorithm from the very beginning of the "Real World Haskell" book. And a perimeter function just for the sake of implementing it.
Why did the authors define the Direction datatype when it only acts like
Ordering?
Is there any more elegant way to traverse through lists considering pairs?
Any suggestions would be appreciated
import Text.Printf
import Data.List (sortBy)
data Point = Point Int Int deriving (Show, Eq, Ord)
turn :: Point -> Point -> Point -> Ordering
turn (Point x2 y2) (Point x1 y1) (Point x3 y3) = compare ((x2 - x1) * (y3 - y1) - (y2 - y1) * (x3 - x1)) 0
polarSort :: [Point] -> [Point]
polarSort points = sorted where
pivot = minimum points
sorted = pivot:(sortBy (\a b -> turn b pivot a) . filter (pivot /=)) points
grahamScan :: [Point] -> [Point]
grahamScan points = grahamScan' rest [p2, p1] where
(p1:p2:rest) = polarSort points
grahamScan' [] l = l
grahamScan' (p:pointsLeft) (s1:s2:selectedPoints) = case turn s2 s1 p of
GT -> grahamScan' (p:pointsLeft) (s2:selectedPoints) -- the turn is right, so we discard last selected point
_ -> grahamScan' pointsLeft (p:s1:s2:selectedPoints) -- the turn is left, so we add another point to the list of selected ones
dist :: Point -> Point -> Double
dist (Point x1 y1) (Point x2 y2) = sqrt (fromIntegral ((x2 - x1)^(2 :: Int) + (y2 - y1)^(2 :: Int)))
perimeter :: [Point] -> Double
perimeter points@(p0:_) = dist p0 pn + rest where
walk :: [Point] -> (Double, Point)
walk [a] = (0, a)
walk (p1:p2:ps) = (dist p1 p2 + next, lastPoint) where
(next, lastPoint) = walk (p2:ps)
(rest, pn) = walk points
main = do
_ <- getLine
content <- getContents
putStrLn $ printf "%.1f" ((perimeter . grahamScan . map ((\([x, y]) -> Point x y) . map read . words) . lines) content)
Intcoordinates? \$\endgroup\$Direction, the reason is semantics. Ordering means something different than direction and the meaning should be reflected in naming. \$\endgroup\$