HaskellでAOJの問題を解いた(ITP1_5)
AOJ の Introduction to Programming を解いたときのメモです。
前回: HaskellでAOJの問題を解いた(ITP1_3~4) - お布団宇宙ねこ
今回解いた問題
- ITP1_5
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 文が多用されており大変読みにくかったですね...。