読者です 読者をやめる 読者になる 読者になる

お布団宇宙ねこ

Haskell ねこ

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

AOJ Haskell Programming

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

前回: HaskellでAOJの問題を解いた(ITP1_7)

今回解いた問題

8_A Toggling Cases

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

import qualified Data.Char as Char (isUpper, isLower, toUpper, toLower)

main = putStrLn . toggleCases =<< getLine

toggleCases :: String -> String
toggleCases [] = []
toggleCases (x:xs)
    | Char.isLower x = Char.toUpper x : toggleCases xs
    | Char.isUpper x = Char.toLower x : toggleCases xs
    | otherwise      = x : toggleCases xs

与えられた文字列の小文字と大文字を入れ替えるだけの問題です。

文字判定と変換はすべて Data.Char モジュールがやってくれます。今回入力を別の型に変換する必要がなかったので =<< を使って toggleCases 関数に渡すようにしました。 main がワンライナーで書けるとスマートに解けた気がして嬉しいですね。

8_B Sum of Numbers

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

import Control.Applicative ((<$>))
import qualified Control.Monad as Monad (when)
import qualified Data.Char as Char (digitToInt)

main = do
    s <- getLine
    Monad.when (read s /= 0) $ do
        print $ sum $ Char.digitToInt <$> s
        main

与えられた数の各桁の和を計算する問題です(H本でも見たことある気がする)。

入力された文字列のすべての文字に digitToInt で Int 型に変換してそれを sum で集計するだけです(特に難しいことはなかった...)。

8_C Counting Characters

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

import Control.Applicative ((<$>))
import qualified Control.Monad as Monad (forM_)
import qualified Data.Char as Char (toLower)
import qualified Data.List as List (sort, group)
import qualified Data.Map as Map (Map, fromList, lookup)

type AlphabetCountMap = Map.Map Char Int

main = do
    s <- getContents
    let acl = alphabetCounts $ groupAlphabet s
    Monad.forM_ ['a'..'z'] $ \c -> do
        putStrLn $ case Map.lookup c acl of
            Nothing -> c : " : 0"
            Just n  -> c : " : " ++ show n

groupAlphabet :: String -> [String]
groupAlphabet s = List.group . List.sort $ filter (\x -> x `elem` ['a'..'z']) $ Char.toLower <$> s

alphabetCounts :: [String] -> AlphabetCountMap
alphabetCounts cs = Map.fromList $ [(head c, length c) | c <- cs]

与えられた文字列に含まれる各アルファベットの数をカウントして出力する問題です。

やりたいことは単純そうなのですが実装をどうしようか結構悩んで、愚直にやるなら文字列を一から走査して各アルファベットの数を State モナドで保持しておく...みたいなことを考えましたが、やりたいことの割に複雑な処理になりそうだったので止めました(主にモナド変換子あたり)。

最終的に次のような実装に落ち着きました。

  1. 入力された文字列を、同じ文字同士が隣り合うように並べ替えて、グルーピングする
  2. 1 のリストを、アルファベットとその数のタプルでリスト化する
  3. 2 のリストを走査して各アルファベットの数を出力する

アルファベットの数を保持するのに今回は Data.Map を組み込んでみました。今までは型シノニムを定義するだけで特に走査関数を用意するようなことをしてなかったのですが、 Data.Map の lookup 関数便利ですね。速度のことは特に考えてないです。

8_D Ring

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

import qualified Data.List as List (elemIndices)

main = do
    s <- getLine
    p <- getLine
    putStrLn $ if elemFromRing (List.elemIndices (head p) s) s p then "Yes" else "No"

elemFromRing :: [Int] -> String -> String -> Bool
elemFromRing [] _  _ = False
elemFromRing (x:xs) s p
    | pickFromRing [x..(x + length p - 1)] s == p = True
    | otherwise                                   = elemFromRing xs s p

pickFromRing :: [Int] -> String -> String
pickFromRing [] _ = []
pickFromRing (n:ns) xs = xs !! (n `mod` length xs) : pickFromRing ns xs

リング状の文字列(以下 S )を受け取って、そこから任意の文字列(以下 P )が作れるかどうかを判定する問題です。 P が、時計回りに連続した文字を取ってきて作れるかどうかというところが肝なので解き方を考えるのに結構苦労しました。

解き方は次のような流れでやりました。

  1. P の先頭が、 S のどの位置に現れるのかを調べる
  2. 1で調べた位置をもとに、そこから P が作れるか調べる
  3. 作れたならそこで終わり、そうでなければ次の位置に進む

(流れだけ書いてみると意外と単純だった...?)

ポイントとしては、「リング状」の文字列から探すというのをどう実現するかというところ。今回は pickFromRing 関数で、P の先頭が出現する位置から P の長さをリストで引数として与えていますが ( [x..(x + length p - 1)] の部分)、このリストの各要素(位置)をもとに S から文字を取ってくるときに、各要素に対して n `mod` ( S の長さ) で余数を計算してその数で文字を取り出すようにしています。こうすることで下の例のように、 S の長さを超えた位置でも、余数を使うことで位置を折り返して取得することができます。

*Main> let s="vanceknowledgetoad"
*Main> length s
18
*Main> pickFromRing [16..22] s
"advance"

まとめ

Haskell で AOJ を解くのもだいぶ慣れてきて少しは Haskell と仲良くなれた感じがしてきたので、そろそろ次のステップに進もうと思います。具体的には JSON Web API でも作ってみようかなあと。なので AOJ 編はこの辺りで一旦終わりにします。それでは。

github.com

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

AOJ Haskell Programming

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

前回: HaskellでAOJの問題を解いた(ITP1_6)

今回解いた問題

7_A Grading

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

import Control.Applicative ((<$>))
import qualified Control.Monad as Monad (when)

main = loopGrading

loopGrading :: IO ()
loopGrading = do
    result <- map (read :: String -> Int) . words <$> getLine
    Monad.when (any (/= (-1)) result) $ do
        putStrLn $ grading result
        loopGrading

grading :: [Int] -> String
grading [m,f,r]
    | m == (-1) || f == (-1) = "F"
    | total >= 80            = "A"
    | total >= 65            = "B"
    | total >= 50 || r >= 50 = "C"
    | total >= 30            = "D"
    | otherwise              = "F"
    where total = m + f

試験の点数によってAやBなどの成績をつけるありふれた問題です。ガードと where 節を使うと綺麗に書ける気がします。

7_B How many ways?

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

import Control.Applicative ((<$>))
import qualified Control.Monad as Monad (when)

main = do
    nx <- getLine' $ map read . words
    Monad.when (any (/= 0) nx) $ do
        print $ length $ combination nx
        main

getLine' :: (String -> a) -> IO a
getLine' f = f <$> getLine

combination :: [Int] -> [[Int]]
combination [n,x] = [[a,b,c] | a <- [1..n], b <- [a..n], c <- [b..n],
                                a + b + c == x, a /= b, a/= c, b /= c]

組み合わせの問題もリスト内包表記で表現することができます。重複無しにするためには b と c の始めの要素を1つ前の変数の要素から始めることと、それぞれの変数の要素が一致しないように述語を入れることで実現できます。

また今回は getLine で入力を受け取る部分を String -> a の関数を受け取る関数にしてみました。こう書くことで型推論によって (read :: String -> Int) みたいなことをいちいち書かなくても済む...ような気がします(型推論がどこまで面倒を見てくれるのかよくわかっていない)。

7_C Spreadsheet

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

import Control.Applicative ((<$>))
import qualified Control.Monad as Monad (forM, replicateM)

type Row = [Int]
type Spreadsheet = [Row]

main = do
    [r,c] <- getLine'
    xs <- Monad.replicateM r $ getLine'
    mapM_ (putStrLn . printFormat) =<< newSpreadsheet (c+1) xs

getLine' :: IO [Int]
getLine' = map read . words <$> getLine

printFormat :: Row -> String
printFormat xs = unwords $ show <$> xs

newSpreadsheet :: Int -> Spreadsheet -> IO Spreadsheet
newSpreadsheet n xs = do
    let xs' = zipWith (++) xs $ rowSums xs
    return $ xs' ++ columnSums n xs'

rowSums :: Spreadsheet -> Spreadsheet
rowSums = map $ (:[]) . sum

columnSums :: (Monad m) =>Int -> Spreadsheet -> m Row
columnSums n xs = Monad.forM [0..(n-1)] $ \i -> return $ sum $ map (!! i) xs

入力として与えられるエクセル表のような数列に、各行の最後の列としてその行の合計値を、各行の最後の行としてその列の合計値を追加した表を出力するという問題でした。

rowSums で各行の行の合計値を計算した後に columnSums で各列の列の合計値を計算するようにしています。本当はモナド必要ないのに繰り返しを forM を使って計算させるの違和感しかないので、何か別の良い方法ないですかね...。畳み込みは今回だと与えられたリストに対して処理を行うわけではないため適切ではないと思い使いませんでした。

7_D Matrix Multiplication

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

import Control.Applicative ((<$>), (<*>))
import qualified Control.Monad as Monad (replicateM)
import qualified Data.List as List (transpose)
import qualified Data.List.Split as Split (chunksOf)

type Row = [Int]
type Matrix = [Row]

main = do
    [n,m,l] <- getLine'
    a <- Monad.replicateM n getLine'
    b <- Monad.replicateM m getLine'
    let result = toMatrix l $ dotProduct <$> a <*> List.transpose b
    mapM_ (putStrLn . printFormat) result

getLine' :: IO [Int]
getLine' = map (read :: String -> Int) . words <$> getLine

printFormat :: Row -> String
printFormat xs = unwords $ show <$> xs

toMatrix :: Int -> [Int] -> Matrix
toMatrix n xs = Split.chunksOf n xs

dotProduct :: Row -> Row -> Int
dotProduct xs ys = sum $ zipWith (*) xs ys

6_D Matrix Vector Multiplication では行列と列ベクトルの積を計算する問題でしたが、今回は行列同士の積を計算する問題です。行列Aと行列Bの積は、行列Aの行と、行列Bの転置行列の列の内積で計算することができます。行列の転置には transpose という便利な関数が Data.List モジュールにあるためそれを使いました。

どうして転置行列を使うことで行列の積が計算できるのかは別途証明する必要がありそうです...。(その辺りの文献が見当たらなかったので)

参考文献

github.com

「fripSide concert tour 2016-2017」に行ってきた

ライブ 南條愛乃 fripSide

fripSide concert tour 2016-2017 -infinite synthesis 3-supported by animelo mix」の神奈川県公演に行ってきました。 fripSide のライブは今回が初参加でした。

fripSide の南條さんは常にクールに歌い上げる印象があり、個人的にはソロ活動の南條さんの方が好みだったのではじめは追ってなかったのですが、CD や BD などで fripSide に触れていくうちにライブの気運が高まってきたので行くことにしました。

感想

BD などで fripSide のライブは観ていたので長時間のライブになるんだろうなあと思っていましたが、約3時間のライブは体験してみるとあっという間でした(体感時間的には半分の1時間半くらいの感覚でした)。南條さんが、ぼーっとしてるとあっという間にライブが終わってしまうよとはじめの方で言っていましたが本当にその通りになった...。曲の繋がりで客席の流れを作るためにコード感を重要視したセットリストを作っている、とパンフレットのインタビューで八木沼さんが答えていたのを読んで、その効果がこういったところにも出ているのかなと思ったりしました。

曲は、タイアップ曲である「only my railgun」や「sister's noise」、「Two souls -toward the truth」なんかはもちろん盛り上がるし好きなのですが、「Answer」のようなバラードもバンドの加わったライブで聴くと良いなあと思ったのでした。

間の MC だと、ドラムの八木さんがゴリラとして紹介されてて八木沼さんからバナナをもらっていたり、FFXVやトリコの話をし始めたらいつものオタクの南條さんのテンションになっていたりして面白かったですね。

来年の SSA ツアーファイナルにも参加する予定なので楽しみです。

『Effective Ruby』を読んだ

Ruby 読書メモ

Effective Ruby

Effective Ruby

なぜ読んだか

Ruby は一応書けるけれど、実践的なコードを書くことになったときに発生する問題を解決するためのベストプラクティスや、より深いイディオムについてまだまだ足りない部分があると感じていて、Ruby の中級者向け以上の書籍を読みたかったので本書を読みました。

感想

各章の項目にその項目で言いたい結論が書かれているので、読み進める前の頭の整理にもなるし、何よりすべて読み終えた後で参照したくなったときに目次を見ればどの項目を読めばいいのか分かるのが良いと思いました。

例えば

第3章 コレクション

項目17 nilスカラーオブジェクトを配列に変換するには、 Array メソッドを使おう

のような。

普段 Ruby を書く上で意識していなかった継承階層の組み立て方や super の括弧有り無しでの挙動の違いから、構造化データの表現には Struct を使うのがよいとか reduce でコレクションの畳み込みを使おうなど、なるほどと思えるテクニックもあり面白く読み進めることができました。

ただ、メタプログラミングの章については、この本の性質上メタプログラミング全体について書いてあるわけではなかったので、理解が追いつかない部分がありいまいち自分の中で消化できていない感じがしました。 『メタプログラミング Ruby』 を読んでから出直したいと思います。

あと、この書籍とはあまり関係がないのですが、本書の「第1章 Ruby に身体を慣らす」で言及しているように、 Ruby では定数がミュータブルであることだったりオブジェクトが nil になる可能性を常に考慮しないといけないことは、 Haskell を学習した後では煩わしさをよく感じるようになりました(自分の中に Static おじさんの存在を感じる...)。そんなこともありつつ、特異な Ruby の思想を取り上げている第1章やテストの章はわかるわかる〜と思いながら読んでました。

まとめ

本書を読んだことで Ruby の様々なテクニックを知ることができて Ruby と少し仲良くなれた気がしました。 何か問題にぶち当たったときや、別の書籍を読み終わった後に改めて読み返したいと思います。

楠田亜衣奈 1st Live Blu-rayリリース記念LIVEに行ってきた

ライブ 楠田亜衣奈 声優

楠田亜衣奈さんの 1st Live Blu-ray 「Eternal Precious Wave」 リリース記念LIVEの昼の部に行ってきました。今まで行くタイミングがなかったので、楠田さんのソロライブはこれが初めてでした。

最後尾列だったので、楠田さんの顔を認識できずにはじめ見たときは楠田さん?という感じでしたが、ライブが始まってみると、とにかくよく動くしダンス技術も健在で、自分のよく知っている楠田さんで安心しました。MCで発言した「みんなTシャツ似合わないから」とズバッというところも楠田さんらしいなあと。

ライブで披露された曲の中では、個人的にはアップテンポの曲よりもバラード調の「群青シネマ」や「My yesterdays」の方が好みだと分かりましたし、楠田さんの声質に合ってる感じがしました。(アコースティックバージョンだったのもあるかもしれませんが)

ほかにも最後尾列から俯瞰して観ていたせいかもしれませんが、会場にいたファンの方々も全体的に大人しい雰囲気だったのも印象的でした。

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

AOJ Haskell Programming

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

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

今回解いた問題

6_A Reversing Numbers

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

import Control.Applicative

main = do
    getLine
    xs <- map (read :: String -> Int) . words <$> getLine
    putStrLn $ unwords $ show <$> reverse xs

入力として受け取った数列を逆順に出力するだけだったので、 reverse 関数を使って簡単に解けました。 [5,4,3,2,1] のようなリストを "5 4 3 2 1" のように出力するのに各要素に show を適用してから unwords で連結すると上手くいきます。

6_B Finding Missing Cards

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

import Control.Applicative
import qualified Control.Monad as Monad

type Card = (String, Int)

main = do
    n <- read <$> getLine
    hc <- map (toCard . words) <$> Monad.replicateM n getLine
    mapM_ (putStrLn . fromCard) $ missingCards hc

toCard :: [String] -> Card
toCard [p,r] = (p, read r)

fromCard :: Card -> String
fromCard (p,r) = p ++ " " ++ (show r)

missingCards :: [Card] -> [Card]
missingCards hc = [ (p,r) | p <- ["S","H","C","D"], r <- [1..13], not $ (p,r) `elem` hc ]

すべての要素から入力として与えられた要素を除いた要素のみを出力すればよい、と考えると、リスト内包表記を使うことでスマートに書けました。カードを表現する型がタプルのままだと分かりづらいと思い type を使って別の名前を付けています。

6_C Official House

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

import Control.Applicative ((<$>))
import qualified Control.Monad as Monad (forM_)
import qualified Control.Monad.Trans as Trans (lift)
import qualified Control.Monad.State as State (execStateT, modify)
import qualified Data.List.Split as Split (chunksOf)

type Room = (Int, Int, Int, Int)

main = do
    n <- getLine
    rs <- putRooms (read n)
    let rs' = Split.chunksOf limitRoom $ zipWith (++) (repeat " ") (show . getValue <$> rs)
    Monad.forM_ [1..(limitBuilding*limitFloor)] $ \bf -> do
        putStrLn $ concat $ rs' !! (bf-1)
        if ((bf `mod` limitFloor == 0) && bf /= (limitBuilding*limitFloor))
            then putStrLn "####################"
            else return ()

putRooms :: Int -> IO [Room]
putRooms n = (`State.execStateT` officialHouse) $ do
    Monad.forM_ [1..n] $ \_ -> do
        room <- Trans.lift $ toRoom . map (read :: String -> Int) . words <$> getLine
        State.modify (`updateRoom` room)

limitBuilding = 4
limitFloor    = 3
limitRoom     = 10

officialHouse :: [Room]
officialHouse = [ (b,f,r,0) | b <- [1..limitBuilding],
                              f <- [1..limitFloor],
                              r <- [1..limitRoom] ]

toRoom :: [Int] -> Room
toRoom [b,f,r,v] = (b,f,r,v)

getValue :: Room -> Int
getValue (_, _, _, v) = v

updateRoom :: [Room] -> Room -> [Room]
updateRoom [] _ = []
updateRoom (room1@(b1,f1,r1,v1):rs) room2@(b2,f2,r2,v2)
    | (b1,f1,r1) == (b2,f2,r2) = (b1,f1,r1,(v1 + v2)) : rs
    | otherwise                = room1 : updateRoom rs room2

officialHouse ですべての部屋の情報を作成し、入力として入居・退去の情報を受け取る度に State モナドを使って部屋情報を更新するようにしています。 putRooms 関数では State モナドと IO モナドを同時に扱う必要があったのでモナド変換子を使っています。 State モナドを使わなくても解けた気がしますが、勉強のために今回は使った感じです。

それとこの問題から、モジュールは使う関数のみをインポートするようにしました。

6_D Matrix Vector Multiplication

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

import Control.Applicative ((<$>))
import qualified Control.Monad as Monad (forM, replicateM)

type Vector = [Int]
type Row = [Int]
type Matrix = [Row]

main = do
    [n,m] <- getLine'
    a <- Monad.replicateM n getLine'
    b <- toVector <$> Monad.replicateM m getLine'
    mapM_ print $ vectorAndMatrixProduct a b

getLine' :: IO [Int]
getLine' = map (read :: String -> Int) . words <$> getLine

toVector :: [[Int]] -> Vector
toVector = concat

vectorAndMatrixProduct :: Matrix -> Vector -> Vector
vectorAndMatrixProduct xs ys = toVector $ do
    Monad.forM [0..(length xs - 1)] $ \i -> dotProduct (xs !! i) ys

dotProduct :: Row -> Vector -> Row
dotProduct xs ys = return $ sum $ zipWith (*) xs ys

行列( Matrix )は二重のリストとして、ベクトル( Vector )はリストとして表現しました。ベクトルと行列の積は、 forM を使って行列の二重リストから行番号ごとにリストを取得し、ベクトルのリストと sum $ zipWith (*) で計算するようにしています。

この問題の応用である 7_D Matrix Multiplication では、行列の転置を使うことでもう少しスマートに書くことができました。

参考文献

github.com

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

AOJ Haskell Programming

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