銀行員からのRailsエンジニア

銀行員からのRailsエンジニア

銀行員から転身したサービス作りが大好きなRailsエンジニアのブログです。個人で開発したサービスをいくつか運営しており、今も新しいサービスを開発しています。転職して日々感じていること、個人開発サービス運営のことなどを等身大で書いていきます。

当ブログではアフィリエイト広告を利用しています

Rubyでのデザインパターンの適用例(ストラテジーパターン、テンプレートメソッドパターン)

デザインパターンって実際に適用するのなかなか難しいですよね..

僕も一通り学んできたのですが、なかなか実務のコードでうまく使えなくて悩んでます。

すごくシンプルな Rubyのコードで、デザインパターンが適用できるケースを考えてみたので宜しければ参考にしてみてください。

f:id:ysk_pro:20200503115503p:plain

元々のコード

例えば、何の変哲もないこんなクラスがあったとします。

class Sample
  def method_a
    # ・・・
    common
  end

  private

  def common
    # ・・・
    process_a
    # ・・・
  end

  def process_a
    # ・・・
    puts 'processAだよ'
  end
end

「# ・・・」となっているところでは、様々な処理を行なっているものとします。

こんな感じで実行すると

Smaple.new.method_a

当然こんな結果にあります。

processAだよ

簡単ですね!

機能追加でコードに変更が入る

この何の変哲もないクラスに、機能追加で publicな method_b を追加することになりました。

method_b内では、commonメソッドとほぼ同じですが下記の点だけが違ったメソッドを使いたいです。
<common メソッドと必要となったメソッドの異なる点>
common メソッドでは process_a メソッドを呼び出しているが、必要となったメソッドでは process_b を呼び出す必要がある。

ひとまず、どう呼び出しを切り替えるかは置いておいて、とりあえず必要となる method_b メソッドと process_b メソッドを追加してみました。

未完成のコード

class Sample
  def method_a
    # ・・・
    common
  end

  # 追加するメソッド
  def method_b
    # ・・・
    # ここにcommonメソッドっぽいやつを入れたい
  end

  private

  def common
    # ・・・
    process_a
    # ・・・
  end

  def process_a
    # ・・・
    puts 'processAだよ'
  end

  # 追加したメソッド
  def process_b
    # ・・・
    puts 'processBだよ'
  end
end

ここで考えます。

どのように実装するとよいでしょうか。


ぜひ、自分だったらどのような実装をするか考えてみてください。


いくつかの実装方法が考えられますね。

実装例① 〜愚直にいくぜ〜

class Sample
  def method_a
    # ・・・
    common_a
  end

  def method_b
    # ・・・
    common_b
  end

  private

  def common_a
    # ・・・
    process_a
    # ・・・
  end

  def common_b
    # ・・・
    process_b
    # ・・・
  end

  def process_a
    # ・・・
    puts 'processAだよ'
  end

  def process_b
    # ・・・
    puts 'processBだよ'
  end
end

common メソッドを、method_a で使うものを common_a、method_b で使うものを common_b と分けてみました。(もはや common では無いですね 笑)

次のように実行すると

Sample.new.method_a
Sample.new.method_b

このような結果となり、正しく動作します。

processAだよ
processBだよ

機能としてはうまくいきました!

ただ、common_a / common_b メソッドは、process_a / process_b メソッド の呼び出し以外の箇所は全く同じなので、同じ処理が2回ずつ書かれてしまっています..

これでは修正漏れなどが起こってしまいそうですね。

違うやり方を考えてみましょう。

実装例② 〜引数によってメソッドを切り替えてみた〜

class Sample
  def method_a
    # ・・・
    common(:a)
  end

  def method_b
    # ・・・
    common(:b)
  end

  private

  def common(a_or_b)
    # ・・・
    send("process_#{a_or_b}")
    # ・・・
  end

  def process_a
    # ・・・
    puts 'processAだよ'
  end

  def process_b
    # ・・・
    puts 'processBだよ'
  end
end

先ほどと同じように実行すると、正しく動作しました。

common メソッドが引数を取るようにし、引数に process_a または process_b メソッドのどちらを呼ぶかの情報(:a / :b)を渡して、それを使って動的に process_a / process_b メソッドを呼び出しています

commonメソッドの共通部分は1つのままなので、パターン①より良さそうですね。

さらに違うやり方を考えてみます。

実装例③ 〜ブロックを使ってみた〜

class Sample
  def method_a
    # ・・・
    common { process_a }
  end

  def method_b
    # ・・・
    common { process_b }
  end

  private

  def common
    # ・・・
    yield
    # ・・・
  end

  def process_a
    # ・・・
    puts 'processAだよ'
  end

  def process_b
    # ・・・
    puts 'processBだよ'
  end
end

同じように実行すると、問題なく動作します。

commonメソッドにブロックを渡して、yield で実行することによって処理を分けてみました。

これもスッキリしていて良さそうですよね。

ここから、デザインパターンを適用したリファクタリングを考えてみます。

実装例④ 〜ストラテジーパターン〜

class Sample
  def method_a
    # ・・・
    common(SampleA.new)
  end

  def method_b
    # ・・・
    common(SampleB.new)
  end

  private

  def common(a_or_b_instance)
    # ・・・
    a_or_b_instance.process
    # ・・・
  end
end

class SampleA
  def process
    # ・・・
    puts 'processAだよ'
  end
end

class SampleB
  def process
    # ・・・
    puts 'processBだよ'
  end
end

同じように実行すると、問題なく動作します。

パターン①〜③と比べて思い切って構造を変えてみました。

method_a、method_b メソッドの変化する部分である processメソッド を SampleA、SampleB クラスに移動させ、common メソッドへの引数として SampleA、SampleB クラスのインスタンスを渡して、渡されたインスタンスの process メソッドを呼び出すことで処理を分けています。

クラスを分離することで、コードの見通しも良くなったと思います。

パターン④はデザインパターンの1つである、「ストラテジーパターン」を使っています。

実装例⑤ 〜テンプレートメソッドパターン〜

class Sample
  def method_a
    # ・・・
    common
  end

  def method_b
    # ・・・
    common
  end

  private

  def common
    # ・・・
    process
  end
end

class SampleA < Sample
  def process
    # ・・・
    puts 'processAだよ'
  end
end

class SampleB < Sample
  def process
    # ・・・
    puts 'processBだよ'
  end
end

次のように実行すると問題なく動作します。

SampleA.new.method_a
SampleB.new.method_b

Sampleクラスから、SampleA・SampleBクラスをして、継承したクラスのインスタンスを使うことで、process メソッドの処理を分けています。

継承を使って違和感のないクラス構成であればスッキリしていて良さそうですね。

パターン⑤はデザインパターンの1つである、「テンプレートメソッドパターン」を使っています。

おわりに

ここまで読んでいただきありがとうございます。

同じコードについて5通りに書き方をしてみました。

パターン①〜⑤のどれが一番優れているかではなく、選択肢をいくつか持っていることによって状況に一番合った実装を選べるのかなあ、と思います。

自分の実装の幅を広げてくれるのでデザインパターンを知っているといいですね。

今回使ったデザインパターンについてはこちらの記事で紹介しているので、ご興味ある方は是非ご覧ください。

ストラテジーパターンについてはこちら
ysk-pro.hatenablog.com

テンプレートメソッドパターンについてはこちら
ysk-pro.hatenablog.com

【Rubyによるデザインパターンまとめ15】メタプログラミング

コードの品質向上のため、Rubyデザインパターンを解説した名著である Rubyによるデザインパターン で紹介されているデザインパターンを1つずつまとめており、今回が第15弾です。(毎週1つが目標です!)

前回の記事(DSLドメイン特化言語)のまとめ)はこちらです。
【Rubyによるデザインパターンまとめ14】DSL(ドメイン特化言語) - 銀行員からのRailsエンジニア

今回は メタプログラミング についてまとめました。

attr_reader と同じ機能を、メタプログラミングを使って実装しながら説明しています。

f:id:ysk_pro:20200314140337p:plain

メタプログラミングとは

必要なコードを全て書いておくのではなく、実行時にプログラムに基づいて作り出します。

文章での説明よりも実際のコードを見た方が分かりやすので、以下のコードをご覧ください。

コード

Rubyのコードではお馴染みの「attr_reader」と同じ機能をメタプログラミングで実装してみましょう。

説明は不要だと思いますが、attr_reader はゲッターメソッドとも呼ばれ、クラスに

attr_reader :name

と記載するだけで、

def name
  @name
end

このように、インスタンス変数を取得するメソッドを定義してくれる便利な機能です。

attr_reader という同じ名前だと分かりづらいので、同じ機能を持った「metapro_reader」をメタプログラミングを使って実装します。

class Object
  def self.metapro_reader(name)
    code = "def #{name}
              @#{name}
            end"
    class_eval(code)
  end
end

class User
  metapro_reader :name

  def initialize(name)
    @name = name
  end
end

なんとこれだけです。

次のように実行すると

user = User.new('メタプロくん')
puts user.name

次の結果になります。

メタプロくん

「metapro_reader :name」のところをコメントアウトすると、このコードは当然エラーします。


コードについて説明していきます。

Objectクラスは全てのクラスの最終的な親クラスで、Objectクラスにメソッドを定義するとどこからでも呼び出すことができます。

本家の attr_reader もそうなのですが、metapro_reader はただのクラスメソッドです。

class_eval メソッドがキモで、引数として渡された文字列をクラス内でRubyプログラムとして実行してくれます。これにより、動的にメソッド(今回でいう metapro_reader)を定義することができます。


※ 本家の attr_reader メソッドは ObjectクラスにincludeされているModuleモジュールに書かれているのですが、効率化のためにRubyではなく C で書かれています。

おわりに

ここまで読んでいただきありがとうございます。

メタプログラミングと聞くとちょっと怖い気がしますが、Rubyのコード内でも使われている身近なものだと分かると思います。

Rubyによるデザインパターン の中では、attr_reader 以外にも多くの例を用いて説明されていて分かりやすかったので、ご興味ある方は是非合わせてご覧ください。

Rubyによるデザインパターン

Rubyによるデザインパターン

また、以前読んだこちらもメタプログラミングについて多く説明がされておりとても面白かったです。

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

次回は、Convention over Configurationについてまとめます。(次回でついに最終回です!)

来週も頑張ります!

(追記)
Convention over Configuration についてまとめました!
是非合わせてご覧ください。
ysk-pro.hatenablog.com

【Rubyによるデザインパターンまとめ14】DSL(ドメイン特化言語)

コードの品質向上のため、Rubyデザインパターンを解説した名著である Rubyによるデザインパターン で紹介されているデザインパターンを1つずつまとめており、今回が第14弾です。(毎週1つが目標です!)

前回の記事(インタプリタパターンのまとめ)はこちらです。
【Rubyによるデザインパターンまとめ13】インタプリタパターン - 銀行員からのRailsエンジニア

今回は DSLドメイン特化言語 についてまとめました。

Railsのコードで実際に使われている箇所をピックアップして説明しています。
f:id:ysk_pro:20200307105417p:plain

DSLドメイン特化言語)とは

ある特定の問題を解決するために専用の言語を定義するパターンです。

Railsでは、Validations・ActiveRecord・Rake・RSpecなど幅広く使われています。

文章の説明よりも実際のコードを見た方が分かりやすので、以下のコードをご覧ください。

コード

Railsのコードのmodelでよく目にする、以下のようなバリデーションを実装したコードはDSLを活用しています。

class Item < ActiveRecord::Base
  validates :title, length: { maximum: 40 }
end

上記のvalidatesは、Railsのコードの中の以下メソッドを呼び出しています。

def validates(*attributes)
  defaults = attributes.extract_options!.dup
  validations = defaults.slice!(*_validates_default_keys)

  raise ArgumentError, "You need to supply at least one attribute" if attributes.empty?
  raise ArgumentError, "You need to supply at least one validation" if validations.empty?

  defaults[:attributes] = attributes

  validations.each do |key, options|
    key = "#{key.to_s.camelize}Validator"

    begin
      validator = key.include?("::") ? key.constantize : const_get(key)
    rescue NameError
      raise ArgumentError, "Unknown validator: '#{key}'"
    end

    next unless options

    validates_with(validator, defaults.merge(_parse_validates_options(options)))
  end
end

rails/validates.rb at 526dd6472b3f3ab72ecc2538067b6c94f395fa36 · rails/rails · GitHub

バリデーションのために専用の言語/記法(例: validates :title, length: { maximum: 40 } )を作ることによって可読性を上げています。

おわりに

ここまで読んでいただきありがとうございます。

Rubyによるデザインパターン の中では、ファイルバックアップ機能を作成するオリジナルのコードを使って説明されていて分かりやすかったので、ご興味ある方は是非合わせてご覧ください。

Rubyによるデザインパターン

Rubyによるデザインパターン

次回は、メタプログラミングをまとめます。

来週も頑張ります!

(追記)
メタプログラミングについてまとめました!
是非合わせてご覧ください。
ysk-pro.hatenablog.com

【Rubyによるデザインパターンまとめ13】インタプリタパターン

コードの品質向上のため、Rubyデザインパターンを解説した名著である Rubyによるデザインパターン で紹介されているデザインパターンを1つずつまとめており、今回が第13弾です。(毎週1つが目標です!)

前回の記事(ビルダパターンのまとめ)はこちらです。
【Rubyによるデザインパターンまとめ11】ファクトリメソッドパターン - 銀行員からのRailsエンジニア【Rubyによるデザインパターンまとめ12】ビルダパターン - 銀行員からのRailsエンジニア

言葉での説明よりもコードを見た方が分かりやすいので、サンプルコードでの説明をメインにしています。

今回は インタプリタInterpreter)パターン についてまとめました。

f:id:ysk_pro:20200229160054p:plain

インタプリタパターンとは

クラスで表現した文法規則で構文を解析し、その結果得られた手順に基づいて処理を実行するパターンです。

Interpreter」は「通訳」という意味です。

文章の説明よりも実際のコードを見た方が分かりやすので、以下のサンプルコードをご覧ください。

サンプルコード

文字列をプラスマイナスできるプログラムを考えます。

class Word
  def initialize(value)
    @value = value
  end

  def execute
    @value
  end
end

class Plus
  def initialize(first, second)
    @first = first
    @second = second
  end

  def execute
    @first.execute + @second.execute
  end
end

class Minus
  def initialize(first, second)
    @first = first
    @second = second
  end

  def execute
    index = @first.execute =~ /#{@second.execute}/
    second_index = index + @second.execute.length
    @first.execute[0,index] + @first.execute[second_index..-1]
  end
end

class Interpreter
  def self.parse(input)
    @waiting_second_word = false
    words = []
    operations = []
    input.split.each do |value|
      if value =~ /^[^+-].*/ && !@waiting_second_word
        words << Word.new(value)
      else
        if symbol = operations.pop()
          first = words.pop
          second = Word.new(value)
          case symbol
          when /\A\+/
            words << Word.new(Plus.new(first, second).execute)
          when /\A\-/
            words << Word.new(Minus.new(first, second).execute)
          end
          @waiting_second_word = false
        else
          @waiting_second_word = true
          operations << value
        end
      end
    end
    words.pop.execute
  end
end

次のように実行すると

puts Interpreter.parse("おはよう + ございます - ござい")

このような結果になります。

おはようます

サンプルコードはこちらを参考にさせていただきました。
GitHub - piscolomo/ruby-patterns: Examples of Patterns in Ruby

おわりに

ここまで読んでいただきありがとうございます。

Rubyによるデザインパターン の中でも、ファイル検索のプログラムを作る過程を通じて説明されており分かりやすかったので、ご興味ある方は是非合わせてご覧ください。

Rubyによるデザインパターン

Rubyによるデザインパターン

次回は、DSLドメイン特化言語)をまとめます。

来週も頑張ります!

(追記)
DSLドメイン特化言語)についてまとめました!
是非合わせてご覧ください。
ysk-pro.hatenablog.com

【Rubyによるデザインパターンまとめ12】ビルダパターン

コードの品質向上のため、Rubyデザインパターンを解説した名著である Rubyによるデザインパターン で紹介されているデザインパターンを1つずつまとめており、今回が第12弾です。(毎週1つが目標です!)

前回の記事(ファクトリメソッドパターンのまとめ)はこちらです。
【Rubyによるデザインパターンまとめ11】ファクトリメソッドパターン - 銀行員からのRailsエンジニア

言葉での説明よりもコードを見た方が分かりやすいので、サンプルコードでの説明をメインにしています。

今回は ビルダ(Build)パターン についてまとめました。

f:id:ysk_pro:20200222105019p:plain

ビルダパターンとは

オブジェクトを作るのに大量のコードを書かなければいけない場合などに、オブジェクトの生成のためのコードを別のクラス(ビルダクラス)に分離するパターンです。

つまり、オブジェクトの生成手順(コード上ではコンストラクタ)を外出しします。複雑なオブジェクトの生成手順を分離することで、構造がシンプルになり、再利用性も高めることができます

文章の説明よりも実際のコードを見た方が分かりやすので、以下のサンプルコードをご覧ください。

サンプルコード

ユーザーのオブジェクトを作るプログラムを考えてみましょう。

まずはビルダパターンを使わずに実装してみます。

class User
  attr_accessor :first_name, :last_name, :birthday, :gender, :status, :email, :password

  def initialize(first_name, last_name, birthday, gender, status, email, password)
    @first_name = first_name
    @last_name = last_name
    @birthday = birthday
    @gender = gender
    @status = status
    @email = email
    @password = password
  end
end

このように実行すると、

user = User.new('テスト', '太郎', '1991-08-11', 1, 1, 'test@example.com', 'pass1234')
p user

次のようにオブジェクトができていることが分かります。

#<UserBuilder:0x00007f911d911f08 @user=#<User:0x00007f911d911ee0 @first_name="テスト", @last_name="太郎", @gender=1, @status=1, @email="test@example.com", @password="pass1234">>

このコードはオブジェクトの生成時に大量のパラメータを渡す必要があり、今後新しいパラメータが追加された場合などには、どんどん複雑になり分かりにくくなってしまいます

これをビルダパターンを使って書き換えてみましょう。

class User
  attr_accessor :first_name, :last_name, :birthday, :gender, :status, :email, :password
end

class UserBuilder
  attr_reader :user

  def initialize
    @user = User.new
  end

  def set_name(first_name, last_name)
    @user.first_name = first_name
    @user.last_name = last_name
  end

  def set_birthday(birthday)
    @user.birthday = birthday
  end

  def set_as_man
    @user.gender = 1
  end

  def set_as_woman
    @user.gender = 2
  end

  def set_as_active
    @user.status = 1
  end

  def set_login_credectiols(email, password)
    @user.email = email
    @user.password = password
  end
end

次のように実行すると先ほどと同じ結果となり、ユーザーのオブジェクトを作ることができました。

user_builder = UserBuilder.new
user_builder.set_name('テスト', '太郎')
user_builder.set_birthday('1991-08-11')
user_builder.set_as_man
user_builder.set_as_active
user_builder.set_login_credectiols('test@example.com', 'pass1234')
user = user_builder.user
p user

こうすることでオブジェクトの生成手順が分かりやすくなり、今後ユーザーと近いオブジェクトを作る際にはビルダクラスを共通化することもできるので再利用性も高まりました

今回のサンプルコードは、こちらのサイトを参考にさせていただきました。英語ですがとても分かりやすくて勉強になりました。
Builder design pattern in Ruby - kkempin’s dev blog - Medium

おわりに

ここまで読んでいただきありがとうございます。

Rubyによるデザインパターン の中でも、ビルダクラスを使ってリファクタリングしていく形式で分かりやすかったので、ご興味ある方は是非合わせてご覧ください。

Rubyによるデザインパターン

Rubyによるデザインパターン

次回は、インタプリタInterpreter)パターンをまとめます。

来週も頑張ります!

(追記)
インタプリタパターンについてまとめました!
是非合わせてご覧ください。
ysk-pro.hatenablog.com

【Rubyによるデザインパターンまとめ11】ファクトリメソッドパターン

コードの品質向上のため、Rubyデザインパターンを解説した名著である Rubyによるデザインパターン で紹介されているデザインパターンを1つずつまとめており、今回が第11弾です。(毎週1つが目標です!)

前回の記事(シングルトンパターンのまとめ)はこちらです。
【Rubyによるデザインパターンまとめ10】シングルトンパターン - 銀行員からのRailsエンジニア

言葉での説明よりもコードを見た方が分かりやすいので、サンプルコードでの説明をメインにしています。

今回は ファクトリメソッド(Factory Method)パターン についてまとめました。

f:id:ysk_pro:20200209075739p:plain

ファクトリメソッドパターンとは

オブジェクトの生成をファクトリメソッドに任せることで、クラス名を指定せずにオブジェクトを生成することができるデザインパターンです。

コードの責務を分割して、保守しやすいコードになることがメリットです。

ファクトリ(Factory)とは「工場」という意味で、ファクトリメソッド = オブジェクトを生産するメソッドというイメージです。

文章の説明よりも実際のコードを見た方が分かりやすので、以下のサンプルコードをご覧ください。

サンプルコード

Hashで与えられるデータを指定した形式(json または csv)で出力するプログラムを考えてみましょう。
まずはファクトリメソッドパターンを使わずに実装してみます。

require 'json'

class Report
  def self.generate(data, type)
    case type
    when 'json'
      data.to_json
    when 'csv'
      data.keys.join(',') + "\n" + data.values.join(',')
    end
  end
end

次のように実行すると

data = {age: 28, hobby: 'golf'}
puts Report.generate(data, 'json')
puts '---'
puts Report.generate(data, 'csv')

実行結果はこのようになります。

{"age":28,"hobby":"golf"}
---
age,hobby
28,golf

このコードの問題点は、Reportクラスが 以下の2つの責務を持ってしまっている点です。

  • 対応する形式(jsoncsv)の定義
  • それぞれの形式に変換するロジック

複数の責務を持ってしまっていることは、有名なSOLID原則の「S」Single Responsibility Principle:単一責任の原則 に反しますね。

このサンプルコードでは対応する形式の種類が少なく、変換のロジックもシンプルなため問題が無いように感じますが、形式の種類が増えたりロジックが複雑になると、可読性が低く保守しづらいコードになってしまいます。

これをファクトリメソッドパターンを使って書き換えてみましょう。

class Formatter
  def self.type(type) # これがファクトリメソッド!
    case type
    when 'json'
      JsonFormatter.new
    when 'csv'
      CsvFormatter.new
    end
  end
end

class JsonFormatter
  def format(data)
    data.to_json
  end
end

class CsvFormatter
  def format(data)
    data.keys.join(',') + "\n" + data.values.join(',')
  end
end

class Report
  def self.generate(data, type)
    Formatter.type(type).format(data)
  end
end

実行方法、結果は先ほどと同じです。

先ほど課題であった、Reportクラスが持ってしまっていた2つの責務を以下のように分けることができました。

  • 対応する形式の定義 → Formatterクラス
  • 変換ロジック → JsonFormatter/CsvFormatterクラス

対応する形式を増やす場合や、変換ロジックに修正が入った場合には、FormatterクラスやJsonFormatter/CsvFormatterクラスを修正すればよく、Reportクラスの修正は不要になりました。

Reportクラスの責務はレポートを出力することであり、レポート出力に関係ない変換ロジックなどの修正でReportクラスを変更しなくてよくなり、保守性が上がっていると思います。

またこれはGoFの原則の1つ「変わるものを変わらないものから分離する」にも則っています。

今回のサンプルコードは、こちらのサイトを参考にさせていただきました。英語ですがとても分かりやすくて勉強になりました。
Ruby - Factory Method pattern - Ruby Blog

おわりに

ここまで読んでいただきありがとうございます。

これまでのブログと形式を変えて、デザインパターンを使っていないコードとその問題点を提示してから、デザインパターンでその問題点を解決していく」という形式で書いてみました。
個人的にはこの方が分かりやすい気がしているので、次回からもこの形で書いていこうと思います。

Rubyによるデザインパターン の中でも、ファクトリメソッドを使っていないコードをファクトリメソッドを使って改善してく流れの説明が分かりやすかったので、ご興味ある方は是非合わせてご覧ください。

Rubyによるデザインパターン

Rubyによるデザインパターン

次回は、ビルダ(Builder)パターンをまとめます。

来週も頑張ります!

(追記)
ビルダパターンについてまとめました!
是非合わせてご覧ください。
ysk-pro.hatenablog.com

AtCoderで緑色になりました!【やってきたことまとめ】

こんにちは。先週末についに AtCoder緑色 になりましたーーー!!始めた頃から緑色を最初の目標にしていたのでめっちゃ嬉しいです!!

2019/9にAtCoderを始めて、5ヶ月かかりました...!

緑色になってテンションの高いうちに、ここまでやってきたことなどをまとめてみます。 

f:id:ysk_pro:20200121214811p:plain

そもそもAtCoderとは?

AtCoderのホームページにある説明です。

AtCoderは、世界最高峰の競技プログラミングサイトです。
リアルタイムのオンラインコンテストで競い合うことや、
3,000以上の過去問にいつでもチャレンジすることができます。

AtCoderトップページより引用)

競技プログラミングの定義ってなんだろと思って、ウィキペディアで調べてみました。

競技プログラミングでは、参加者全員に同一の課題が出題され、より早く与えられた要求を満足するプログラムを正確に記述することを競う。

ウィキペディア/競技プログラミングより引用)

与えられたお題をクリアするプログラムを誰が最初に書けるか、みたいな感じです。

実際の問題を見たほうが雰囲気が分かると思うので引用します。

直近(2020/1/19)に行われた AtCoder Beginner Contest 152(初心者向けのコンテスト)の一番簡単な問題(A問題)です。

<問題文>
高橋君は、プログラミングコンテスト AXC001 に参加しており、問題 A にコードを提出しました。
この問題にはN個のテストケースがあり、すべてのテストケースに正解した場合のみ提出は AC となります。
高橋君の提出は、N個のテストケースのうち M個のテストケースに正解しました。
高橋君の提出が AC となるか判定してください。

<制約>
1 ≤ N ≤ 100
0 ≤ M ≤ N
入力はすべて整数である。

<入力>
入力は以下の形式で標準入力から与えられる。

N M

<出力>
高橋君の提出が AC となる場合は Yes, そうでない場合は No と出力せよ。

書いたプログラムで入力を受け取って、正しい出力を返せれば正解となります。

様々な言語で回答することができ、僕が Ruby で書いたコードはこちらです。

  1. n, m = gets.chomp.split.map(&:to_i)
  2. if n == m
  3.  puts 'Yes'
  4. else
  5.  puts 'No'
  6. end

すごい簡単ですよね。

コンテストでは、こんな感じの問題が複数出題されて、早くたくさん解いた順にランキングがつけられます。

 

一見複雑な問題を考察して、シンプルなアルゴリズムに落とし込んで解けたときは最高に気持ちいいです...!

 

緑色ってどのくらいのレベルなの?

AtCoderでは、オンラインで行われるコンテストに出場すると、結果に応じてレーティングがつき、レーティングによって色が変わります。

上から順に「赤・橙・黄・青・水・緑・茶・灰」となっており、緑は下から3番目です。

AtCoderの社長(競技プログラミングがとても強い方でもあります)のブログでは緑色のレベル感について次のような説明がされていました。

緑色になれれば、競技プログラミングに熱心に取り組んでいる人」と考えて問題ないでしょう。要求レベルも決して低くなく、出場回数が足りないとマイナス補正がかかるため、運だけで到達することはまず出来ないラインです。他社アルゴリズム力判定サービスだと、上位1%の最高ランクが付く実力です。(あくまで「アルゴリズム力部分だけであることに注意してください)

 

AtCoderをはじめたきっかけ

元々学生時代は数学が好きだったこともあり(相関があるのか不明ですが)、競技プログラミングというものに興味がありました。

時間ができたときに、試しに少しやってみよーと思ってやってみたら一瞬でハマっていました

みんなでやるとより楽しいので、今では会社内外で布教活動もしています(このブログもその一環かな)。

 

やってきたこと

前置きが長くなってしまいましたがここからが本題です。

コンテストへの出場

AtCoderでは、ほぼ毎週末(土曜 or 日曜の 21:00 からが多い)に初心者向けのコンテストが行われており、予定が無い日は極力参加しています。

コンテスト後にAtCoderの中の方がYoutubeで解説をしてくださるのですが、解法のみではなく考え方から教えてくださり、とっても分かりやすいので自分が解けなかった問題の解説は必ず見ています。

また、コンテスト後は全員の提出したコードが見れるので、同じ言語で提出されたコードを見て「こんな書き方あるのか〜」と参考にしています。

今まで参加してきたコンテストの結果はこんな感じです。14回のコンテストに出てきました。

f:id:ysk_pro:20200120215545p:plain

パフォーマンスというのが、そのコンテスト1回でのレーティングのようなものらしいです。

少しは成長しているようなしていないような。

レーティング推移も載せておきます。

f:id:ysk_pro:20200121214943p:plain

 

AtCoderの過去問

AtCoder Problems という神サービスを使って、過去問を毎日やっています。

自分が過去問の何割くらいを解いたのかであったり、何日連続で問題に取り組めているかを見れるのでモチベーションの維持にも役立ちます。

f:id:ysk_pro:20200120213900p:plain

赤枠で囲ったのですが、現在37日連続で問題に取り組めています。いい感じ。

f:id:ysk_pro:20200120213913p:plain

GitHubのように草も生やせます。楽しい。

 

蟻本

競技プログラミング界では超有名な プログラミングコンテストチャレンジブック(通称 蟻本)にも取り組みました。

難しくて初級編までしか読めておらず、初級編の理解も怪しいので、定期的に読み返そうと思っています。

 

モチベーションの維持

仲間を見つける

会社でAtCoderを一緒に始める仲間を見つけることができたので、slackでatcoderチャンネルを作って毎日ワイワイ盛り上がっています。

同時期に始めたので、レーティングを競い合ったりできて楽しいです。

会社外でも、個人開発のコミュニティ「運営者ギルド」のslackで最近ワイワイやり始めました。

 

おわりに

AtCoderめっちゃ楽しいです!

敷居も低いです!

ご興味ある方は是非一度チャレンジしてみてください!

 

次は水色目指して頑張ります。