大ちゃんの駆け出し技術ブログ

RUNTEQ受講生のかわいいといわれるアウトプットブログ

【無職に転生 ~ 就職するまで毎日ブログ出す_10】【Ruby】競技プログラミングで使えそうな記述

はじめに

こんにちは、大ちゃんの駆け出し技術ブログです。

タイトルにあるとおり【無職に転生 ~ 就職するまで毎日ブログ出す】というチャレンジをしています!!!!昨日までは就活するまで本気出すでしたが、これだとまるで就活後は頑張らないのかと思われてしまいそうで、、、大人気アニメのタイトルのまるパクリチャレンジです。

https://i.gyazo.com/3a02b7aae4e5e538130d4bb199b55dc7.png

RailsやらRubyやらSQLやらその他Webの知識やらが色々と抜け落ちているのを感じており、知識の定着のためにもアウトプットする機会を増やすためです。加えて、退職して文字通り無職に転生しましてプロニートになり、毎日時間に余裕ができたので引き締めるためにも毎日投稿を思い至りました!

【投稿内容】

  • SQLの難しい処理 (副問合せ、JOINとか複雑な処理が書けない)
  • Rails全般 (純粋に必要な知識が多すぎる、網羅的な理解が足りない)
  • Rubyのあまり使わないメソッドや記述方法 (あまり重要ではないけど特に)
  • Web知識全般 (クッキーやら、セッションやらなんとなくで理解しているものの自分の言葉で説明できない)
  • 書籍 (スタートアップ企業に勤めるので、自分が会社に与える影響やパフォーマンスを高めるためビジネス書を読んでいきます。)

本日やること

昨日より競技プログラミングに取り組んでいます。railsを使っている時には使わないようなメソッドや記述がたくさん出てきたのでここらへんでまとめておきます。主に使えると思ったものは以下の6つです。自分はまだ初めて2日しか経っていないので完全始めた人向けに書きますので、競技プログラミング熟練者からしたら否定されそうですが何卒ご容赦の程よろしくお願いします。

  • 複数入力
  • each_with_index
  • index/find_index
  • transpose
  • gsub
  • ブロックの連番

複数の値を入力

gets.split

これはpaizaで頻出というかこれを使えないと多くの課題をクリアできません。頻出と言わず問題を解く上で必須です。なぜかというとpaizaや競技プログラミングでは一度の入力で二つの数を定義しなければいけない時があります。例えば、速度と重さの変数x, yがあるとすると、それらを入力する際には下記のように一度の入力で定義する必要があるのです。

x y

それを可能にするのがgets.splitです。getsメソッドは入力値をそのまま文字列として出力します。

gets
123
=> "123\n"

# 後ろに改行コードが含まれてしまうのでchompメソッドと併用されることが多い
gets.chomp
123
=> "123"

splitはレシーバーの文字列を分割して配列にして返します。引数を指定しなければスペース区切りで分割できます。

"abc def ghi".split
=> ["abc", "def", "ghi"]

上述した二つの組み合わせで二つの変数を一度に入力できます。

gets.split
10 20
# => ["10", "20"]

しかし、gets.splitでは文字列の値しか取得できません。上記のように数字を出力してもそれは文字列として扱われます。そこでmapメソッドを用いて数値の入力を可能にします。

gets.split.map(&:to_i)
10 20
# => [10, 20]

each_with_index

このメソッドすごく便利です。with_indexとあるように繰り返し処理時にインデックス番号もブロック内で使うことができるメソッドです。

%i(1 2 3).each_with_index do |n, idx|
    p n, idx
end
=> 1, 0
=> 2, 1
=> 3, 2

利用機会としては、例えばXがある時にこのXと等しい要素は配列の何番目にあるかを出力する処理などで使えます。

array.each_with_index do |n, idx|
  puts idx + 1 if n == X
end

eachだと要素しか処理に使うことができませんが、インデックス番号も処理で使えることで複雑な処理を可能にします。


index/find_index

レシーバーの要素の中に引数で指定した要素が含まれていれば、その要素のindex番号を返すメソッドです。find_indexはindexのエイリアスなのですが、find_indexの方が記述的に挙動が分かりやすいので自分はこちらを使用しています。

[1,2,3,4,5].find_index(4)
# => 3

使うタイミングとしては、条件とマッチしたインデックス番号を出力したい時に配列に格納する方法をとりました。

index_arrays << arrays.index(array)

transpose

二次元配列を行列とみなして行と列を入れ替えるメソッドです。

ary = [[1, 2], [3, 4], [5, 6]]
p ary.toranspose
# => [[1,3,5], [2,4,6]]

競技プログラミングだと入力の順番が決められています。そして繰り返し同じ数のす値を入力して二次元配列に格納する処理も出てきます。例えばある処理をN回行うといった時に、下記のようにN回文のデータが必要な場合に二次元配列にして格納される処理があります。

N.times.map (gets.split.map(&:to_i))
数字a 数字b 数字c 数字d
数字e 数字f 数字g 数字h
数字i 数字j 数字k 数字l
数字m 数字n 数字o 数字p

しか上述した配列の順番を以下のように行と列を入れ替えたい場合もあると思います。

[[数字a, 数字e, 数字i, 数字m],[数字b, 数字f, 数字j, 数字n],・・・]

そんな時はtransposeメソッドを使えば行と列を入れ替えればOKです。transposeメソッドは行と列の中の要素数が一致しないとエラーを起こしますが、競技プログラミングでは繰り返し入力する時の数は決まっているので使えるタイミングはかなりありそうです。

N.times.map (gets.split.map(&:to_i)).transpose

gsub

文字列を置換する便利なメソッドです。

文字列パターンを第一引数で指定して、マッチしたものを置換する文字を第二引数で指定します。

p 'abcdefg'.gsub(/def/, '!!')          # => "abc!!g"
p 'abcabc'.gsub(/b/, '<<\&>>')         # => "a<<b>>ca<<b>>c"
p 'xxbbxbb'.gsub(/x+(b+)/, 'X<<\1>>')  # => "X<<bb>>X<<bb>>"
p '2.5'.gsub('.', ',') # => "2,5"

複数のパターンを指定することもできます。

下の文字は母音をすべて空文字に置換するコードです。

puts gets.chomp.gsub(/a|i|u|e|o|A|I|U|E|O|/, "")
aaaaiiiooobbb
# => "bbb"

ブロックの連番

これは知らなかったのですが、ブロックの中の引数を1、2、_3と連番にして取り出すことができるんです。

gets.split.map(&:to_i).then{puts _3, _2, _1}
1 2 3
3
2
1

これは伊藤淳一さんの記事でも出されていました。

サンプルコードでわかる!Ruby 2.7の主な新機能と変更点 Part 1 - 番号指定パラメータ(numbered parameter) - Qiita

本来であれば繰り返し処理はインデックス番号を昇順に処理が進んでいきます。よって、インデックス番号が大きい要素はかなり後にならないと処理が始まらないのです。そこで連番を設けることで早く処理に使いたい要素を連番で指定できます。