みくにまるのブログ

意識低い系ブロガー、みくにまるが送るブログ。

HaskellでAOJ ITP1_6_B不足しているカードの発見

なくなったカードの発見 | プログラミング入門 | Aizu Online Judge

nput

最初の行に太郎が持っているカードの枚数 n (n ≤ 52)が与えられます。

続いて n 組のカードがそれぞれ1行に与えられます。各組は1つの空白で区切られた文字と整数です。文字はカードの絵柄を表し、スペードが'S'、ハートが'H'、クラブが'C'、ダイヤが'D'で表されています。整数はそのカードのランク(1 ~ 13)を表しています。

Output

足りないカードをそれぞれ1行に出力して下さい。各カードは入力と同様に1つの空白で区切られた文字と整数です。出力するカードの順番は以下のとおりとします:

絵柄がスペード、ハート、クラブ、ダイヤの順番で優先的に出力する。
絵柄が同じ場合は、ランクが小さい順に出力する。

初見での方針は
全カードのリストを作って
そこと与えられたカードとの差分を取る事。

ところが全カードのリストを作るスマートな方法が分からない。
["S","H","C","D"] と[1..13]を組み合わせたいがググって方法を探る。
日本語でググってもあまり情報が出て来ないので
hackageのData.Listと睨めっこする事にした。
しかしそんな都合のいい操作は見当たらず・・・

allcards = zip (repeat "H") [1..13] ++
           zip (repeat "D") [1..13] ++
           zip (repeat "S") [1..13] ++
           zip (repeat "C") [1..13]

仕方なく作ったのが上の関数。
・・・いくら何でもダサすぎる。
多少格好をつける為にも共通部分をくくる事にした。

allcards = cards "H" ++
           cards "D" ++
           cards "S" ++
           cards "C"
             where
               cards s = zip (repeat s) [1..13]

まだくくれる。

allcards = concat $ map cards ["H","D","S","C"]
             where
               cards s = zip (repeat s) [1..13]

なんとかそれっぽくなって一安心。


扱いにくいので
タプルをリストにする関数も作成。

tupleToList :: (String, Integer) -> [String]
tupleToList (s,n) = [s, show n]

allcardsList :: [[String]]
allcardsList = map tupleToList allcards


ここで
["D","13"]のようなリストよりも
"D 13"のような一つのStringにした方が更に扱いやすい事に気付く。
という訳でtoupleToListを改造。

tupleToString :: ([Char], Integer) -> String
tupleToString (s,n) = s ++ " " ++ show n

allcardsList :: [String]
allcardsList = map tupleToString allcards

こういった方針転換を簡単に出来るのがHaskellの良さだ。
出力では順番にとなっているので
最初に全カードを作る時の関数を順番通りになるように変更。

import Data.List ((\\))

allcards :: [([Char], Integer)]
allcards = concat $ map cards ["S","H","C","D"]
             where
               cards s = zip (repeat s) [1..13]


tupleToString :: ([Char], Integer) -> String
tupleToString (s,n) = s ++ " " ++ show n

allcardsList :: [String]
allcardsList = map tupleToString allcards

main = do
  s' <- getContents
  let s = tail $ lines s'
  putStr $ unlines $ allcardsList \\ s

最終的に上記のコードで通ったので
答え合わせに達人達のコードを拝見

r = [x | x <- [(s : ' ' : (show n)) | s <- "SHCD", n <- [1..13]]

http://judge.u-aizu.ac.jp/onlinejudge/review.jsp?rid=1716949#1
リスト内包表記でスマートに書けたようだ。

Prelude> [x | x <- [(s : ' ' : (show n)) | s <- "SHCD", n <- [1..13]]]
["S 1","S 2","S 3","S 4","S 5","S 6","S 7","S 8","S 9","S 10","S 11","S 12","S 13","H 1","H 2","H 3","H 4","H 5","H 6","H 7","H 8","H 9","H 10","H 11","H 12","H 13","C 1","C 2","C 3","C 4","C 5","C 6","C 7","C 8","C 9","C 10","C 11","C 12","C 13","D 1","D 2","D 3","D 4","D 5","D 6","D 7","D 8","D 9","D 10","D 11","D 12","D 13"]

う〜む、これは美しい。勉強になった。