-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathMain.hs
More file actions
128 lines (109 loc) · 3.57 KB
/
Copy pathMain.hs
File metadata and controls
128 lines (109 loc) · 3.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import qualified Data.Map.Strict as Map
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import Data.List (sortBy)
import Data.Ord (comparing)
import System.IO
import Data.Time.Clock (getCurrentTime, diffUTCTime)
-- 경로 상수
outputPath :: FilePath
outputPath = "outputs.txt"
measurementsPath :: FilePath
measurementsPath = "measurements.txt"
-- 통계 데이터 구조
data Status = Status
{ sMin :: !Int
, sMax :: !Int
, sTotal :: !Int
, sCount :: !Int
} deriving (Show)
-- 수동 정수 파싱
parseIntManual :: T.Text -> Maybe Int
parseIntManual txt = go 0 0
where
len = T.length txt
go !acc !i
| i >= len = Just acc
| otherwise =
let c = T.index txt i
digit = fromEnum c - fromEnum '0'
in if digit >= 0 && digit <= 9
then go (acc * 10 + digit) (i + 1)
else Nothing
-- 라인 파싱 함수 (Text 사용)
parseLine :: T.Text -> Maybe (T.Text, Int)
parseLine line =
case T.breakOn ";" line of
(city, rest) | not (T.null rest) ->
parseIntManual (T.tail rest) >>= \num -> Just (city, num)
_ -> Nothing
-- Status 업데이트 함수 (strict)
updateStatus :: Status -> Int -> Status
updateStatus !status !measurement =
Status
{ sMin = min (sMin status) measurement
, sMax = max (sMax status) measurement
, sTotal = sTotal status + measurement
, sCount = sCount status + 1
}
-- 새로운 Status 생성
newStatus :: Int -> Status
newStatus !measurement =
Status
{ sMin = measurement
, sMax = measurement
, sTotal = measurement
, sCount = 1
}
-- 라인 처리 함수 (꼬리 재귀로 메모리 최적화)
processLines :: Handle -> Map.Map T.Text Status -> IO (Map.Map T.Text Status)
processLines h !acc = do
eof <- hIsEOF h
if eof
then return acc
else do
line <- TIO.hGetLine h
let !newAcc = case parseLine line of
Just (city, measurement) ->
Map.insertWith (\_ old -> updateStatus old measurement)
city
(newStatus measurement)
acc
Nothing -> acc
processLines h newAcc
-- 포맷팅 함수
formatOutput :: (T.Text, Status) -> String
formatOutput (city, status) =
let avg = sTotal status `div` sCount status
in T.unpack city ++ "=" ++
show (sMin status) ++ ";" ++
show (sMax status) ++ ";" ++
show avg ++ "(" ++
show (sTotal status) ++ "/" ++
show (sCount status) ++ ")\n"
-- 메인 솔루션 함수
solution :: FilePath -> IO String
solution path = do
withFile path ReadMode $ \h -> do
hSetEncoding h utf8
!statsMap <- processLines h Map.empty
let sortedList = sortBy (comparing fst) $ Map.toList statsMap
result = concatMap formatOutput sortedList
return result
-- 메인 함수
main :: IO ()
main = do
expectedOutput <- readFile outputPath
startTime <- getCurrentTime
result <- solution measurementsPath
let !_ = length result -- Force evaluation
endTime <- getCurrentTime
let elapsed = diffUTCTime endTime startTime
elapsedMs = realToFrac elapsed * 1000 :: Double
putStrLn $ "Elapsed: " ++ show (round elapsedMs :: Int) ++ "ms"
if expectedOutput == result
then putStrLn "Matched!"
else error "Output does not match expected!"