HaskellでABCを攻略していく ~ABC001C~
問題はこれ ABC001C
風向と風力を入力し、値に応じて風向と風力をレベルに分けて表示する問題ですね。
入力
- 風向 (0 ~ 3600) : Deg
- 風力 (0 ~ 12000) : Dis
出力
- 風向 (1 ~ 3文字の文字列)
- 風力 (1 ~ 12)
実装
レギュラーパターン
- 風力をレベルに変換
- 風向を16方位に変換
- 合わせて表示
イレギュラーパターン
- 風力レベルが0の場合は風向はCになる
ソースコード
windPower :: Int -> Int windPower dis | d < 250 = 0 | d < 1550 = 1 | d < 3350 = 2 | d < 5450 = 3 | d < 7950 = 4 | d < 10750 = 5 | d < 13850 = 6 | d < 17150 = 7 | d < 20750 = 8 | d < 24450 = 9 | d < 28450 = 10 | d < 32650 = 11 | otherwise = 12 where d = dis * 1000 `div` 60 windDir :: Int -> Int -> String windDir deg wp | wp == 0 = "C" | otherwise = dir deg dir :: Int -> String dir deg = let i = length (filter (< deg * 10) [1125,3375..34875]) in ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N"] !! i ans :: Int -> Int -> String ans deg dis = let wp = windPower dis dir = windDir deg wp in dir ++ " " ++ show wp main :: IO () main = do [deg,dis] <- map read . words <$> getLine putStrLn $ ans deg dis
関数
windowPower関数
風力を受け取り風力レベルに変換する関数
windPower :: Int -> Int windPower dis | d < 250 = 0 | d < 1550 = 1 | d < 3350 = 2 | d < 5450 = 3 | d < 7950 = 4 | d < 10750 = 5 | d < 13850 = 6 | d < 17150 = 7 | d < 20750 = 8 | d < 24450 = 9 | d < 28450 = 10 | d < 32650 = 11 | otherwise = 12 where d = dis * 1000 `div` 60
整数(風力 m / min)を受け取り整数(風力レベル)を返す where句では仮引数として受け取ったdisを1000倍した値を60で割った商をd(秒速)とし、dの値でパターンマッチングを行っている。
パターンマッチングではdとビューフォート風力階級の各階級の上限値を1000倍した値との比較いにより風力のレベルを返却するようにしている。
値が小数になり読みにくいため両方の値を1000倍した(100倍でよかった説)
通常0.2m/secは1000倍すると200になるが、50を加算することで小数第二位での四捨五入に対応している。
windDir関数
風向を決定する関数(風力の有無を判定)
windDir :: Int -> Int -> String windDir deg wp | wp == 0 = "C" | otherwise = dir deg
風向と風力を整数値で受け取り、16方位+中央の方向を文字列として返却する。 風力が0の場合は風向はCとなり、それ以外の場合は仮引数degを実引数としdir関数を実行する
dir関数
風向を決定する関数(風向の決定)
dir :: Int -> String dir deg = let i = length (filter (< deg * 10) [1125,3375..34875]) in ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N"] !! i
本問題の入力は0~3600であるが、風向は小数点第二位までを確認するため、10倍し0~36000の値に変換することで、整数値で処理している。 そして、値を順にfilter関数で評価し、境界を探す。 境界を超えるとそこで方角が判明するので、そのindexの番号を用いて方角の文字列を表す配列のindex番号として使用し、方角を返却する。
ans関数
本問題での出力値を文字列として返却する関数
ans :: Int -> Int -> String ans deg dis = let wp = windPower dis dir = windDir deg wp in dir ++ " " ++ show wp
wpは風力で、関数windPowerの戻り値 dirは風向で、関数windDirの戻り値
main関数
実行部分です。
main :: IO () main = do [deg,dis] <- map read . words <$> getLine putStrLn $ ans deg dis
[deg,dis] <- map read . words <$> getLine
をさらに分解
getLineは一行の入力を受け付けます。
受け取った文字列を <$> (fmap)
を用いてwords関数に適用します。
words関数はスペース区切りの文字列をリストにして返却する関数です。
ここで[deg,dis] <- map read . りすと
って感じになります。
入力した2つの数字を変換したリストに対してmap関数でreadを適用させ、二つの整数値にし、degとdisに値を束縛する。