In today’s Programming Praxis, our task is to calculate the total distance traveled by Santa based on data published by NORAD. Let’s get started, shall we?
First, some imports:
import Data.List.HT import Text.HJson import Text.HJson.Query
The easiest version of the algorithm to calculate the distance between two coordinated can be found here. I’ve made a few small adjustments to get rid of some duplication. The Scheme solution rounds off the result, but I don’t believe that is correct. Granted, it doesn’t result in a big deviation (3 miles on a total of almost 200000), but rounding off should be saved until the end.
dist :: RealFloat a => (a, a) -> (a, a) -> a dist (lat1, lng1) (lat2, lng2) = let toRad d = d * pi / 180 haversin x = sin (toRad $ x / 2) ^ 2 a = haversin (lat2 - lat1) + cos (toRad lat1) * cos (toRad lat2) * haversin (lng2 - lng1) in 2 * 6371 * atan2 (sqrt a) (sqrt (1 - a))
Rather than hunting through the string ourselves for the coordinates, we use a Json library.
coords :: Json -> [(Double, Double)] coords = map ((\[JString lat, JString lng] -> (read lat, read lng)) . getFromKeys ["lat", "lng"]) . getFromArr
The total distance can be easily calculated by summing the distances between the subsequent points of the route.
totalMiles :: RealFloat a => [(a, a)] -> Int totalMiles = round . (* 0.621371192) . sum . mapAdjacent dist
All that’s left to do is to read in the route and print out the result.
main :: IO () main = either print (print . totalMiles . coords) . fromString . drop 16 =<< readFile "santa.txt"
Initially there was a much larger difference between my version and the provided answer. It turns out that the route on page 2 is different from the current route published by NORAD, resulting in a difference of about 2000 miles. Using the same route as the Scheme version reduced this to 3, due to the rounding error present in the provided solution.