HaskellでAtCoderのA問題
はじめに
AtCoderのA問題を解いていきます.
Haskell初心者なのでなんか色々違うかも.
ABC334 A - Christmas Present
解答
main :: IO ()
main = do
[b, g] <- map read . words <$> getLine :: IO [Int]
putStrLn $ if b > g then "Bat" else "Glove"
解説
シンプルにbとgを比較してそれぞれBatとGloveを表示する感じです.
[b, g] <- map read . words <$> getLine :: IO [Int]
入力からスペース区切りの数値を読み取ってます.
わかりやすく括弧を付ければ,
((map read) . words) <$> getLine :: IO [Int]
になります(多分,多分ね).
処理の流れは(多分)以下の通り,
- 入力
-
getLine
: 入力を受け取ってIO String
を返します. -
words
: Stringをスペース(または改行)で区切ってリストにします.IO [String]
になってるはず. -
map read
: 先ほどの[String]
の各要素に対してInt
にパースして[Int]
に詰めなおしてくれます.
正直よくわからないので間違ってるかも.
ちなみに左辺をhoge <-
とすればリストとして取得できます.
[a, b] <-
としたときは順番通りにa, bに代入してくれます(入力とリストの要素数が食い違うとエラーになるみたいです).
putStrLn $ if b > g then "Bat" else "Glove"
まあ,うん,普通ですね.
putStrLn
は引数を出力してくれます.
print
というのもあって,putStrLn $ show
と同じみたいです.
print "Bat"
と書くとBat
ではなく"Bat"
と表示されてしまうので大人しくputStrLn使いましょう.
ABC333 A - Three Threes
解答
main :: IO ()
main = do
n <- readLn :: IO Int
putStrLn $ concat $ replicate n $ show n
解説
C++とかならいくらでも書けるんですが,Haskellわからなすぎてめちゃくちゃ苦戦しました.
nをn個含めたリストを結合しよう作戦です.
n <- readLn :: IO Int
さっきの問題と違って1つの数値だけを読み取る場合はreadLn
が使えます.
多分さっきの書き方で↓でもいけます.
[n] <- map read . words <$> getLine :: IO [Int]
putStrLn $ concat $ replicate n $ show n
replicateとconcatの解説をば
-
replicate a b
: bをa回繰り返したリストを生成します.replicate 3 5
なら[5, 5, 5]
になるってことですね. -
concat a
: リストのリストを1つのリストに結合してくれます.concat [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
なら[1, 2, 3, 4, 5, 6, 7, 8, 9]
にしてくれます.文字列は[Char]
なので,concat ["abc", "def", "ghi"]
は"abcdefghi"
になりますね.
つまりは,
- n = 5のとき,
-
show n
により"5"
にされ, -
replicate n
によって["5", "5", "5", "5", "5"]
になり, -
concat
によって"55555"
にされる.
という感じです.簡単簡単.
ABC332 A - Online Shopping
解答
import Control.Monad
main :: IO ()
main = do
[n, s, k] <- map read . words <$> getLine :: IO [Int]
pq <- replicateM n $ map read . words <$> getLine :: IO [[Int]]
let sp = foldl (\a [p, q] -> a + p * q) 0 pq
let price = if sp >= s then sp else sp + k
print price
解説
答えは簡単に求まるんですが...
いかんせん入力がよく分からない.
pq <- replicateM n $ map read . words <$> getLine :: IO [[Int]]
replicateM n func
というのはfunc(なんかの関数)をn回繰り返すための関数みたいです.
名前の最後のMはMonadのことで,よくわからないですがモナドが関わってくるときはreplicate
の代わりにこっちを使えばいいのかな?
入力の形としては,
P1 Q1
P2 Q2
.
.
.
Pn Qn
なのでPとQの配列を分けたかったんですが,よくわかりませんでした!
pqには[[P1, Q1], [P2, Q2], ... , [Pn, Qn]]
みたいな感じで入力が入ってます.まあいいか.
let sp = foldl (\a [p, q] -> a + p * q) 0 pq
foldl
は,まあreduce
みたいなやつですよね.
説明が難しいので実例で.
P・Qとして
100 1
200 6
300 3
が与えられたとします.つまり
pq = [[100, 1], [200, 6], [300, 3]]
これに例のfoldl (\a [p, q] -> a + p * q) 0 pq
をぶつけてみます.
-
foldl
の第1引数であるラムダ式(\a [p, q] -> a + p * q)
のa
としてfoldl
の第2引数0
,[p, q]
としてfoldl
の第3引数pq
の1番目の要素[100, 1]
が与えられます.
つまりa = 0, p = 100, q = 1
になりますね. - ラムダ式の中身が実行されて
a + p * q = 0 + 100 * 1 = 100
が取り出され,今度はラムダ式のa
に先ほどの計算結果,[p, q]
にpq
の2番目の要素[200, 6]
が与えられます.
つまり今度はa = 100, p = 200, q = 6
です. - ラムダ式の中身が実行されて
a + p * q = 100 + 200 * 6 = 1300
が取り出され,またしてもさっきと同じようにラムダ式に各値が渡されます.
a = 1300, p = 300, q = 3
ですね. - ラムダ式の中身が実行され,
a + p * q = 1300 + 300 * 3 = 2100
が取り出され,もうpq
の最後までたどり着いたので,この値が返り値として戻ってきます.
これで金額の合計が計算できるわけですね.
うん,難しいね.なんなんだこの激ムズパズル.
ABC331 A - Tomorrow
解答
main :: IO ()
main = do
[m, d] <- map read . words <$> getLine :: IO [Int]
[y', m', d'] <- map read . words <$> getLine :: IO [Int]
let d_ans = if d' == d then 1 else d' + 1
let m'' = if d' == d then m' + 1 else m'
let m_ans = if m'' == m + 1 then 1 else m''
let y_ans = if m'' == m + 1 then y' + 1 else y'
putStrLn $ unwords $ map show [y_ans, m_ans, d_ans]
解説
あまり難しいことはしてないので出力だけ.
putStrLn $ unwords $ map show [y_ans, m_ans, d_ans]
文字列のリストをunwordsに渡すと空白区切りの1つの文字列にしてくれます.ありがたい.
ABC330 A - Counting Passes
解答
main :: IO ()
main = do
[_, l] <- map read . words <$> getLine :: IO [Int]
a <- map read . words <$> getLine :: IO [Int]
print $ length $ filter (>= l) a
解説
length $ filter (>= l) a
filter
は第1引数にとる関数を第2引数の配列の各要素にかけ,True
を返すものだけを抜き出す関数です.
l
以上になるものを抜き出して要素数数えて終わり!
ABC329 A - Spread
解答
main :: IO ()
main = do
s <- getLine
putStrLn $ unwords $ map (: []) s
解説
いたって普通の関数に紛れ込む謎記号の正体を暴くべくアマゾンの奥地へと向かった...
map (: []) s
Haskellのリストを生成するには[1, 2, 3]
のようにするのが普通ですが,これは糖衣構文です.
実際は1 : 2 : 3 : []
が正規の方法になっています.
この:
はcons演算子といい,簡単に言えば左辺の値を右辺のリストにぶち込む演算子になっています.
(: [])
は値をとってリストに詰めてくれる関数なんですね.
詳しくは リストとは何か? [ ](空リスト)と: (cons演算子)について とか見てください.
最初はunwords
にs
を直接渡せばいけるかなと思ったんですが,Char
は直接受け取ってくれず,文字列型として1文字ずつリストに詰めないといけませんでした.
ABC328 A - Not Too Hard
解答
main :: IO ()
main = do
[_, x] <- map read . words <$> getLine :: IO [Int]
s <- map read . words <$> getLine :: IO [Int]
print $ sum $ filter (<= x) s
解説
これはもう解説いらんか.
sum
関数はリストの中身全部足してくれる関数です.便利.
ABC327 A - ab
解答
solve :: String -> Bool
solve s = do
let s' = tail s
or $ zipWith (\x y -> x == 'a' && y == 'b' || x == 'b' && y == 'a') s s'
main :: IO ()
main = do
_ <- readLn :: IO Int
s <- getLine
putStrLn $ if solve s then "Yes" else "No"
解説
solve
関数の中身を解説していきます.
let s' = tail s
tail
関数はリストの1番目以外の要素を取り出してくれます.
後々使う.
or $ zipWith (\x y -> x == 'a' && y == 'b' || x == 'b' && y == 'a') s s'
zipWith
関数は2つのリストの同じインデックス同士を演算して1つのリストにしてくれる関数です.例えば,
zipWith (+) [1, 2, 3] [4, 5, 6]
とすれば1番目,2番目,3番目同士がそれぞれ足しあわされて,[5, 7, 9]
という配列が出てきます.
要素数が一致しなかったときは短い方に合わせられ,はみ出した分は無視されます.
先ほど用意したs'
はs
の先頭以外の要素,つまりs'
とs
では各要素が1つづつずれているのです!
これでzipWith関数を使うことで隣り合った2つの要素を比べることができました.
直接インデックスで指定する方法もあると思うのですが,なんかこっちの方がかっこよさそうだったのでこうしました.
ABC326 A - 2UP3DOWN
解答
main :: IO ()
main = do
[x, y] <- map read . words <$> getLine :: IO [Int]
putStrLn $
if x < y
then if y - x < 3 then "Yes" else "No"
else if x - y < 4 then "Yes" else "No"
解説
ないね,解説すること.
おしまい
AtCoderのA問題は言語を浅く使ってみるのにピッタリですね.
気が向いたらB問題編もやるかもしれない.
ひとりごと
記事書くのって大変ですね.
プログラムのコメントも書かないのにZennの記事なんか書けるわけねぇ.
Discussion