Graham scan
Real World Haskell の 3章末の練習問題 12 - Graham scan アルゴリズムの実装 - を解いてみた。
import List import Data.Ord -------- 問 9 -------- -- 方向 data Direction = LeftTurn | RightTurn | Straight deriving (Eq, Show) -------- 問 10 -------- -- 座標 data Point = Point { x :: Double , y :: Double } deriving (Eq, Show) -- 2次元平面上の点の位置関係を計算し、 -- Direction を返す ccw :: Point -> Point -> Point -> Direction ccw a b c | exterior > 0 = LeftTurn | exterior < 0 = RightTurn | otherwise = Straight where exterior = (x b - x a) * (y c - y a) - (x c - x a) * (y b - y a) -------- 問 11 -------- -- ccw のList版 ccws :: [Point] -> [Direction] ccws (p1:p2:p3:ps) = ccw p1 p2 p3 : ccws (p2:p3:ps) ccws _ = [] -------- 問 12 -------- -- 位置でソート sortCoordinate :: [Point] -> [Point] sortCoordinate ps = sortBy cmp ps where cmp a b | y a < y b = LT | y a > y b = GT | x a < x b = LT | x a > x b = GT | otherwise = EQ -- 角度でソート sortAngle :: Point -> [Point] -> [Point] sortAngle p ps = sortBy cmp ps where cmp a b = compare (cot b) (cot a) where cot c = (x c - x p) / (y c- y p) -- Graham scan の準備 gsort :: [Point] -> [Point] gsort ps = let csort = sortCoordinate ps lower = head csort in lower : sortAngle lower (tail csort) -- Direction の List の中に RightTurn があるか判定 turnRight :: [Direction] -> Bool turnRight [] = False turnRight (x:xs) | x == RightTurn = True | otherwise = turnRight xs -- Graham scan grahamScan :: [Point] -> [Point] grahamScan [] = [] grahamScan ps = scan [] (gsort ps) where scan [] (y1:y2:ys) = scan [y1, y2] ys scan xs [] = xs scan xs (y:ys) | turnRight (ccws (xs ++ [y])) = scan (init xs) (y:ys) | otherwise = scan (xs ++ [y]) ys
Point 型は余分だったかな。
追記 (2010/03/24)
turnRight はリストの中に RightTurn があるかを判定しているけど、リストの最後が RightTurn がどうかを判定するだけで大丈夫だと思う。