HaskellでABCを攻略していく ~ABC001C~

問題はこれ ABC001C

風向と風力を入力し、値に応じて風向と風力をレベルに分けて表示する問題ですね。

入力

  • 風向 (0 ~ 3600) : Deg
  • 風力 (0 ~ 12000) : Dis

出力

  • 風向 (1 ~ 3文字の文字列)
  • 風力 (1 ~ 12)

実装

レギュラーパターン

  • 風力をレベルに変換
  • 風向を16方位に変換
  • 合わせて表示

イレギュラーパターン

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に値を束縛する。