Ruby初心者が本を読んだ際の忘備録

Ruby基礎事項

配列・繰り返し処理

  • Rubyではfor文は使わない→eachメソッドを用いる

    numbers = [1,2,3,4]
    sum = 0
    numbers.each do |n|
        sum += n
    end
    • do-endを使わない場合は{}で囲うことも可能
      numbers.each { |n| sum += n }
  • 以下の条件を満たす場合は、&:メソッド名が利用可能

    1. ブロック引数が一つ
    2. ブロックの中で呼び出すメソッドには引数がない
    3. ブロックの中では、ブロック引数に対してメソッドを呼び出す以外の処理がない
      [1,2,3,4,5,6].select(&:odd?)
  • 配列に範囲内の値を入れる際はto_aメソッドを用いる

    (1..5).to_a
  • rjustメソッドの利用で、桁数の調整が可能

        '0'.rjust(5)
        '0'.rjust(5, '0')
        '0'.rjust(5, '_')

    デフォルトは空白で桁揃えされているが、第2引数を指定すると空白以外の文字列を埋めることができる。

  • 配列の結合時concatは破壊的、+は非破壊的

  • breakは一番内側のループを抜ける。throw-catchは一番外側のループまで抜ける。例外処理とは関係ない。例外処理はraise-rescueを使用する。

  • redoはその回の繰り返し処理の最初に戻る。実装時には無限ループにならないように、回数のカウントを行う。イタレーションとか行う際に、基準以下ならやり直し、カウントオーバしたら続行みたいなのに使いそう。

    ハッシュ・シンボル

  • ハッシュの記法

    hash = { key1 => value1, key2 => value2 }
  • シンボル

    • 記法
      :シンボルの名前
    • 同じシンボルであれば、全く同じオブジェクトである(.object_idを比較すると同じ)。例えば、大量の同じ文字列と、大量の同じシンボルを作成した際は、シンボルの方がメモリ効率は良い。
    • ただし、シンボルはイミュータブルなので破壊的な変更は不可能。
    • 実用例を見ると、シンボルをハッシュのキーにすることで、文字列よりも高速に値を取り出すことができる。
  • 使用頻度の高いメソッド

    • keys:ハッシュのキーを配列として返す
    • values:ハッシュの値を配列として返す
    • has_key?/key?/include?/member?:ハッシュの中に指定されたキーが存在するかどうか確認するメソッド。
  • 想定外の引数は**に吸収することができる。

    def buy_burger(menu, drink: true, potato: true, **others)

    としておけば、想定外の入力が会った際も、othersにハッシュとして格納される。

  • ハッシュはto_aメソッドを使用することで配列に変換することが可能。逆に配列に対して、to_hメソッドで配列をハッシュに変換することが可能。

  • 自己代入(||=

    limit ||= 10

    は変数limitnilまたはfalseであれば、10を代入する(それ以外はlimitのそのままの値を用いる)という意味になる。

    • Rubyではnilまたはflaseであれば偽、それ以外は真とみなされる。

      正規表現

  • 正規表現と文字列を比較する際には、=~が使用される。マッチすると文字列中のマッチした位置が返る。マッチしなかった場合にはnilが返る。

  • キャプチャにはmatchメソッドを使用する。ただし、そのままmatchメソッドを使用すると配列に連番で格納されてしまうため、「何番目が何の値か」を意識する必要がある。

  • よく使われるメソッド

    • scan:引数で与えた正規表現にマッチする部分を配列にいれて返す。
    • [], slice, slice!:文字列から正規表現にマッチした部分を抜き出す。
    • split:正規表現を渡すと、マッチした文字列を区切り文字にして文字列を分解して、配列として返す。
    • gsub, gsub!:第一引数の正規表現にマッチした文字列を第二引数の文字列で置き換える。
    • キャプチャを用いた場合は\1, \2のようにしてキャプチャした文字列を連番で使用可能
    • 名前付きキャプチャの場合はk<name>のようにして参照できる
    • 第二引数にハッシュを渡して、変換のルールを指定することも可能。
  • \w[A-Za-z0-9_]と同じ意味。

  • 正規表現オブジェクトの作成方法

    • //:文字列内にスラッシュを使用する際はエスケープする必要がある。
    • %r{}, %r!!:囲む文字列次第でエスケープが不要になる。
  • 正規表現オブジェクトのオプション

    • 定義:/正規表現/オプション
    • i:大文字・小文字を無視してマッチングを行う
    • m:任意の文字を表す.が改行文字にもマッチするようになる。
    • x:空白文字(改行文字も)が無視され、#を使って正規表現中にコメントがかけるようになる。
      regexp = /
      \d{3}  # 郵便番号の先頭3文字
      -      # 区切り文字のハイフン
      \d{4}  # 郵便番号の末尾4文字
      /x
  • $で始まる組み込み変数もある。

    • $&:マッチした部分全体
    • $1, $2,...:キャプチャの取得
    • $+:最後のキャプチャ文字列の取得

クラスの作成

  • クラス名は大文字で始める。慣例としてキャメルケースで書く。
  • コンストラクタはinitializeメソッド(外部から呼び出すことは不可能)。
  • インスタンス変数は@で始める
  • インスタンス変数の値を読み書きするメソッドのことを「アクセサメソッド」という(ゲッターとかセッター)。単にインスタンス変数の内容を外部から読み書きするのであれば、attr_accessorというメソッドを使う。
  • インスタンスに含まれるデータは使わないメソッドを定義したい場合もある→クラスメソッド
    • 定義方法:メソッド名の前にself.をつける。
  • テスト実行時Minitestではsetupメソッドを定義すると、テストメソッドの実行前に毎回setupメソッドが呼び出される。
  • セッターメソッドを呼び出したい場合は必ずselfをつける。
  • クラスの継承を行う場合は以下のように定義する。
    class DVD < Product
    end
    • 継承時にメソッドを書き換える場合はスーパクラスと同じ部分は
      super(重複する部分1,重複する部分2)

      と表記することで省略可能

  • メソッドの公開レベル(JavaやC#とは若干異なる
    • public : クラスの外部からも自由に呼び出せる。
    • private : クラスの内部のみで使用可能。クラスの内部でprivateと記載すると、それより下に記載にたメソッドはprivateとなる。ただし、クラスメソッドは対象外。
    • クラスメソッドをproivateにしたい場合は、class << selfの構文を使用する。
    • privateメソッドではself付きで呼び出すとエラーになる。
    • 他の言語と異なり「privateメソッドはそのクラスだけでなくサブクラスでも呼び出せる」。
    • protected : そのメソッドを定義したクラス自身と、そのサブクラスのインスタンスメソッドからレシーバ付きで呼び出せる。
  • クラスの外部から定数を参照する場合は
    クラス名::定数名

    という構文を用いる。

    • 定数の際代入を防ぐ場合には.freezeメソッドを使用する。あまり使わないらしい。
  • クラスをネストさせた場合は
    外側のクラス::内側のクラス

    を使って参照が可能

  • 等値を判断するメソッド
    • equal?object_idが等しい場合にtrueを返す。
    • ==:オブジェクトの内容が等しいかどうかを判定
    • eql?:ハッシュのキーとして2つのオブジェクトが等しいかどうかを判断する。
    • ===:case文のwhen節で使用される。
  • オブジェクト.メソッド名と記述すると、特定のオブジェクトにだけ結びつくメソッドを定義することができる。→特異メソッド

モジュール

  • モジュールの定義
    module モジュール名
    # モジュールの定義(メソッドや定数など)
    end

    定義方法としてはclassに似ているが、モジュールからインスタンスを生成することはできず、ほかのモジュールやクラスを継承することはできない点がクラスとは異なる。

  • モジュールはクラスにincludeして機能を追加することが可能(ミックスイン)。
  • ミックスインする方法としてextendもある。extendを使うとモジュール内のメソッドをそのクラスと特異メソッド(クラスメソッド)とすることが可能。
  • UFO演算子 a<=>b
    • aがbより大きいなら正の整数
    • aとbが等しいなら0
    • aがbより小さいなら負の整数
    • aとbが比較できない場合はnil
  • 実務的にモジュールの使用方法を考える場合は、名前空間の使用時が多い。クラス名の重複を防ぐ。モジュールないにクラスを定義すれば、同名のクラスがあってもモジュール名が異なれば衝突は起きない。使用方法はモジュール名::クラス名で指定。
  • モジュールではミックスインとしても使えて、モジュールの特異メソッドとしても使えるメソッドを定義することができる。module_functionメソッド。
  • モジュールをprependした場合は、ミックスインしたクラスよりも先に、モジュールからメソッドが検索される。
  • refinementsを使用すると、オーバーライドしたライブラリやメソッドのスコープを指定することができる(モジュールもrefine可能)。有効にするときは使いたいクラスでusing モジュール名とする。

例外処理

  • 例外を補足して、処理を続行する場合は
    begin
        # 例外が起きうる処理
    rescue
        # 例外が発生した場合の処理
        # rescueの右に補足したい例外クラス名を記載すると、例外オブジェクトのクラスが一致した場合のみ、例外を補足することができる。
    end
  • 例外が発生した場合、処理を中断して、メソッドの呼び出しを一つずつ戻っていく。メソッド呼び出しを戻る途中にその例外を補足するコードがあれば、そこから処理を続行できる。
    • 通常のプログラムで補足するのは、StandardErrorクラスか、そのサブクラスに限定すべき。
    • 補足する順番(継承関係)に注意しないと、永遠に補足されない例外処理を記載することになる。→サブクラスから順番に記載する。
    • ネットワークエラーのように一時的に発生している問題の対処方法としてretryコマンドがある。実装としては、カウンタ変数を用意して回数に制限をしないと、無限ループになる。
  • rescue => eと書いて、eにエラーメッセージを格納できる。
  • 意図的に例外を発生させる場合はraiseを行う。
  • 例外処理を入れた場合、例外の発生有無にかかわらず実行したい処理はensure節を加える。また、例外が発生しなかった場合の分岐としてelse節も追記可能。
  • rescueは修飾子としても使用可能
    例外が発生しそうな処理 rescue 例外が発生した時の戻り値
  • Rubyでは最後に発生した例外は組み込み変数の$!に格納。バックトレース情報は$@に格納される。コードの可読性の観点から、組み込み変数はあまり使わない。
  • rescue内でraiseすることも可能→rescueでログメール送信などの処理をしたのちに、補足した例外を再度発生させて、プログラム自体は異常終了させる。

yieldとProc

  • yieldはブロックに引数を渡したり、ブロックの戻り値を受け取ったりできる。
    • ブロックをメソッドの引数として、明示的に受け取ることもできる。ブロックを引数として受け取る場合は引数名の前に&をつける。また、そのブロックを実行する場合はcallメソッドを使う。
    • ブロック引数の個数はarityを用いて確認可能。
      def greeting(&block)
      puts 'おはよう'
      text = 
      if block.arity == 1
          yield 'こんにちは'
      elsif block.arity == 2
          yield 'こんに', 'ちは'
      end
      puts text
      puts 'こんばんは'
      end
  • Procクラスはブロックをオブジェクト化するためのクラス。
    • 作成方法
      Proc.new { |a, b| a + b }
      proc { |a, b| a + b }
      ->(a, b) { a + b }
      lambda { |a, b| a + b }
    • Procオブジェクトを実行する方法
      add_proc = Proc.new { |a,b| a + b }
      add_proc.call(10, 20)  # callメソッド
      add_proc.yield(10, 20) # yieldメソッド
      add_proc.(10, 20)      # .()を使う
      add_proc[10, 20]       # []を使う

      デバッグ方法

  • 例外クラスとその原因(概要)
    • NameError:タイポやrequireを忘れている場合
    • NoMethodError:メソッド名のミス、レシーバの方が想定していた型と異なる。レシーバがnilになっている。
    • TypeError:期待しない型がメソッドの引数に渡されたときに発生する。
    • ArgumentError:引数に型が違ったり、期待する値ではなかったりした場合
    • SystemStackError:システムスタックが溢れた時に発生する。
  • Byebugという外部ライブラリ。

その他

  • RubyにはコードからAPIドキュメントを生成するツールが付属している。RDoc用のコメント文を作成して
    rdoc PATH

    実行するとHTML形式のファイルが生成される。

  • 二重コロンとドットの違い

    • 名前空間を区切ったり、定数を参照する場合は二重コロンを用いる
    • メソッドを呼び出す際はドットを使う。
      
      module Sample
      class User
          NAME = 'Alice'.freeze
          def self.hello(name = NAME)
              "Hello, I am #{name}."
          end
      end
      end

    Sample::User::Name #=> "Alice"
    Sample::User.hello #=> "Hello, I am Alice."

  • FileUtilsモジュールは基本的なファイル操作をまとめているもの
  • CSVファイルを読み書きする場合はCSVクラスが使える
  • YAMLもyamlクラスで扱える
  • RakeはRubyで作られているビルドツール。MakeのRuby版。

コメント