お布団宇宙ねこ

にゃーん

HaskellでAOJの問題を解いた(ITP1_5)

AOJ の Introduction to Programming を解いたときのメモです。

前回: HaskellでAOJの問題を解いた(ITP1_3~4) - お布団宇宙ねこ

今回解いた問題

5_A: Print a Rectangle

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_5_A

import Control.Applicative
import qualified Control.Monad as Monad

main = loop

loop :: IO ()
loop = do
    [h,w] <- map (read :: String -> Int) . words <$> getLine
    Monad.when (h /= 0 || w /= 0) $ do
        printRectangle h w
        loop

printRectangle :: Int -> Int -> IO ()
printRectangle h w = do
    Monad.forM_ [1..h] $ \_ -> do
        putStrLn $ concat . take w $ repeat "#"
    putStrLn ""

ある文字列を決められた長さだけ出力するには、 take と repeat を使って無限長のリストから任意の数の要素を取得する方法で上手くできました。と書いている途中で replicate でも同じことができたことに気がつきました...。replicate を使って書くなら replicate w "#" ですね。

5_B: Print a Frame

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_5_B

import Control.Applicative
import qualified Control.Monad as Monad

main = loop

loop :: IO ()
loop = do
    [h,w] <- map (read :: String -> Int) . words <$> getLine
    Monad.when (h /= 0 || w /= 0) $ do
        printFrame h w
        loop

printFrame :: Int -> Int -> IO ()
printFrame h w = do
    Monad.forM_ [1..h] $ \i -> do
        putStrLn $ frameOrDot h w i
    putStrLn ""

frameOrDot :: Int -> Int -> Int -> String
frameOrDot h w i = if (i == 1 || i == h)
                     then concat . take w $ repeat "#"
                     else "#" ++ (concat . take (w-2) $ repeat ".") ++ "#"

A の問題ではただ # を出力するだけでよかったのですが、こちらは長方形の中身を . にする必要があります。 frameOrDot という関数で、受け取った引数の行番号 i が最初か最後なら # のみを連結した文字列を返し、そうでないなら . の両端に # を連結した文字列を返すようにしました。こちらも replicate を使えばすっきりしそうです。

5_C: Print a Chessboard

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_5_C

import Control.Applicative
import qualified Control.Monad as Monad

main = loop

loop :: IO ()
loop = do
    [h,w] <- map (read :: String -> Int) . words <$> getLine
    Monad.when (h /= 0 || w /= 0) $ do
        printChessboard h w
        loop

printChessboard :: Int -> Int -> IO ()
printChessboard h w = do
    Monad.forM_ [1..h] $ \i -> do
        putStrLn $ squareLine w i
    putStrLn ""

squareLine :: Int -> Int -> String
squareLine w i = if (odd i) then
                   takeSquare w "#" "."
                 else
                   takeSquare w "." "#"

takeSquare :: Int -> String -> String -> String
takeSquare w s1 s2 = take w $ concat $ zipWith (++) (repeat s1) (repeat s2)

A, B をさらに発展させた問題で、今度は #..# が交互に組み合わせた長方形を出力する問題です。 takeSquare で文字列を生成する部分は replicate を使っても書けそうですが、 take 3 $ concat $ replicate 3 "#." のようなコードだと replicate で余分な文字列を生成してしまうので厳しそうです。 take で余分な文字列は捨ててしまうので問題はないのですが、無駄がある感じがします。

5_D: Structured Programming

http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_5_D

import Control.Applicative

main = do
    n <- read <$> getLine
    putStrLn $
        concat $ zipWith (++) (repeat " ") (show <$> filterCheckNumber n)

filterCheckNumber :: Int -> [Int]
filterCheckNumber n = [ x | x <- [1..n], (x `mod` 3 == 0) || includeThree x ]

includeThree :: Int -> Bool
includeThree x
    | x == 0          = False
    | x `mod` 10 == 3 = True
    | otherwise       = includeThree $ x `div` 10

C++ で書かれたサンプルプログラムと同じプログラムを実装するという問題でした。プログラムの処理内容は、整数 1 から与えられた整数 n までの間で、ある条件にマッチする数だけを出力するというものです。ある条件というのは3の倍数か includeThree に書かれているガードの条件です。サンプルプログラムには goto 文が多用されており大変読みにくかったですね...。

github.com