🍉

競プロ鉄則 Haskell AtCoder 振り返り2

2024/08/01に公開

A2

問題: N個の整数 A_1,A_2,⋯,A_N の中に、整数Xが含まれるかどうかを判定するプログラムを作成してください。
制約:

  • Nは1以上100以下の整数
  • Xは1以上100以下の整数
  • A_1,A_2,⋯,A_Nは1以上100以下の整数

入力:
N\ X\\
A_1\ A_2\ ⋯\ A_N\\

提出したコード

main :: IO ()
main = do
  a <- getLine
  b <- getLine
  let ls = map read $ words (a <> " " <> b) :: [Int]
  let b = isContain (ls!!1) ((tail.tail) ls)
  let res = if b then "Yes" else "No"
  putStr res
  
isContain :: Int -> [Int] -> Bool
isContain _ [] = False
isContain a (x:xs) = 
  if a==x then True else isContain a xs

結果

AC 時間:2ms

警告

app/Main.hs:6:7: warning: [-Wname-shadowing]
    This binding for ‘b’ shadows the existing binding
      bound at app/Main.hs:4:3
  |
6 |   let b = isContain (ls!!1) ((tail.tail) ls)
  |       ^

ふつーに間違へて 同じ b を別のものに使ってしまった〜
でも エラーにはならなかった

考察

一行目の入力と二行目の入力をスペースでつないでから words函數を適用して 文字列のリストをつくり それら各々を Int型として read してゐます
そもそもNの値は使はないから

let x = (read . last . words) a
let as = (map read . words) b

と個別に定義してもよかったかもしれません
あと isContain の内容は any函數と同じだと思ひます

再提出

main :: IO ()
main = do
  x <- getLine >>= return . read . last . words
  as <- getLine >>= return . map read . words
  let bin = any (==x) (as :: [Int])
  putStr (if bin then "Yes" else "No")

時間: 1ms
となり コードは簡潔で 速くなった

付記

リストに適用する函數で last と tail と !! が出てきてゐます
例へば
last [1,2,3,4]4
tail [1,2,3,4][2,3,4]
(tail . tail) [1,2,3,4][3,4]
[1,2,3,4]!!12
[1,2,3,4]!!01
[1,2,3,4]!!34
[1,2,3,4]!!4 は エラー

isContain :: Int -> [Int] -> Bool
isContain _ [] = False
isContain a (x:xs) = 
  if a==x then True else isContain a xs

について
一行目: isContainといふ函數は Int型 と [Int]型をとって Bool型を返すよ っと宣言してゐます
この行がなくても コンパイラが型を判断してくれて動くと思ふのですが かういふ「型注釈」があると 函數の 「何と何から何をつくる」みたいな 全體像が把握しやすいので 書くのがオススメです
二行目: isContainに渡される ひとつめの値が何であらうが ふたつ目のリストが空リスト(要素が何もない)であれば False(偽)といふ Bool型の値を返します
三行目: リストが空でなければ ひとつめの値を a とし ふたつめのリストを 頭とおしりに分けて x, xs にそれぞれ指定します
例へば リストが [1,2,3,4] であれば x = 1, xs = [2,3,4] となります
四行目: もし a と x が等しければ True(真) といふ Bool型の値を返し さもなくば isContain函數に a と 殘りのリスト xs を渡します
んで any といふ函數は 以上のことを 簡潔に より効率的にやるものです(と理解してゐます)

毒(独)談

「なんとか初心者」っていふ表現って あまり意味をなさない表現だと思ふ
それを言ったところで 相手には「この人がどこまで なんとか を知ってゐるのか」といふことについて 全く分からない
「初心者」といふ言葉が 各々の主観でしか使はれ得ないから 言ふならば この言葉は
「ぼくちん このテーマに あんま自信ないから 間違ったこと言っても ゆるしてちょ」
といふことを傳へたいために使はれてゐる氣がする
子供のころは 自分の考へたことを そのまんま傳へやうとするのに
いつの間にか 人と比べる習慣がついてしまふ
いつしか 思ったことを言ってくれる人が まはりにも少くなって
さらに 「かう思はれるんぢゃ・・・」みたいな妄想が強くなっていく
なんか そんなの ヴァカらしいから 自分の主観を堂々と表現したいな〜 と思ふ今日このごろ

B2

問題: A 以上 B 以下の整数のうち、100 の約数であるものは存在しますか。
制約:

  • 1≤A≤B≤100
  • 入力はすべて整数

入力: A\ B\\

提出コード

main :: IO ()
main = do
  a <- getLine
  let ls = map read $ words a :: [Int]
  let b = isContain (head ls) (last ls) 
  let res = if b then "Yes" else "No"
  putStr res
  
isContain :: Int -> Int -> Bool
isContain i n
  | i > n = False
  | otherwise = 
      if mod 100 i == 0 then True else
          isContain (i+1) n

結果

AC 時間:2ms 警告なし

考察

連續した整数のリストをつくって A2問題みたいに any函數を適用したら 速くなるだらうか

再提出

main :: IO ()
main = do
  [a,b] <- getLine >>= return . map read . words
  let bin = any (\i -> mod 100 i == 0) [a..b] 
  let res = if bin then "Yes" else "No"
  putStr res

結果

AC 時間:1ms

警告

app/Main.hs:3:37: warning: [-Wtype-defaults]
    • Defaulting the type variable ‘b0’ to type ‘Integer’ in the following constraints
        (Read b0) arising from a use of ‘read’ at app/Main.hs:3:37-40
        (Eq b0) arising from a use of ‘==’ at app/Main.hs:4:34-35
        (Integral b0) arising from a use of ‘mod’ at app/Main.hs:4:24-26
        (Num b0) arising from the literal ‘100’ at app/Main.hs:4:28-30
        (Enum b0)
          arising from the arithmetic sequence ‘a .. b’
          at app/Main.hs:4:40-45
    • In the first argument of ‘map’, namely ‘read’
      In the first argument of ‘(.)’, namely ‘map read’
      In the second argument of ‘(.)’, namely ‘map read . words’
  |
3 |   [a,b] <- getLine >>= return . map read . words
  |

速くなった!!
mod a b は a を b で割った余りを出すんだけれど a や b は Integral といふ型クラスに含まれる型である と定義されてゐるから 必ずしも Int型ぢゃない っていふか ここでは Integer型と解釈されてるみたい
なんで ちゃんと 型制約を書かう

main :: IO ()
main = do
  [a,b] <- getLine >>= return . map read . words
  let bin = any (\i -> mod 100 i == 0) [(a::Int)..b] 
  let res = if bin then "Yes" else "No"
  putStr res

これで警告はなくなりました!
ちなみに mod 100 i100に 型制約をつけて (100::Int) としたコードを提出したら テストケースのひとつが 2ms かかってゐました

付記

えっと まづ [a..b] から
例へば [3..9] といふのは [3,4,5,6,7,8,9] といふことです
こんなんでいいかな?
なんか 「aからbまでを連續して並べたリストをつくります」とか言ふより 例を示した方が分かりやすいんぢゃないかって氣がする〜
(\i -> mod 100 i == 0) といふのは それ全體がひとつの函數です〜
この函數は ひとつの値をとって それを i とし mod 100 i の値(100をiで割った余り) と 0 を比べて 等しかったら True 等しくなければ False を返します

isContain :: Int -> Int -> Bool
isContain i n
  | i > n = False
  | otherwise = 
      if mod 100 i == 0 then True else isContain (i+1) n

について
このisContain函數は Int型の値 と Int型の値 をとって Bool型の値を返します
二行目:とる値を それぞれ i n とします
三行目:もし i > n なら(iがnより大きいなら) False を返します
四行目:さうでなければ
五行目:mod 100 iが 0 であれば True を さうでなければ isContain 函數に i+1 と n を入れた結果を返します

ここで一句

くりかへす 日々もわずかに ことなれば
かならず來せし 春のいとしさ

Discussion