🙌

Rubyまとめ

2023/04/22に公開

Rubyまとめ

基礎知識

  • 以下のように、戻り値の一部を捨てることができる。
def test_method
  [10, 20]
end

a, _b = test_method
  • /: エスケープ処理(後の記号の意味をなくしてただの文字列とする)

  • 1_000_000_000と書けば_は無視される

  • &&の優先順位は||より高い

丸め誤差

以下は左項が0.3000~04と丸め誤差があるため、等しくならない。

0.1 * 3 == 0.3 #=>false

そのため、以下のように有理数に変換する必要がある。

0.1r * 3r == 0.3 #=>true

また、メソッドはrationalizeである。

文字列

%! 文字列!でエスケープが必要なく!で文字列を作れる
複数行に渡る時は以下のようにヒアドキュメントを使用する。

str = <<識別子(TEXTなど大文字)
文字列
文字列
識別子

<<-を使うと最後の識別子をインデントさせることができる。

sprintf('%0.3f',1.2)#=>"1.200"というふうにsprintfで表示できる。

数値

  • 基数指示子
基数指示子 内容
0b 2進数
0o 8進数
0x 16進数
  • ビット演算
記号 内容
& ビットごとのAND
| ビットごとのOR
^ ビットごとのXOR
>> 右ビットシフト
<< 左ビットシフト
~ ビットごとのNOT
  • 指数表現
    2e-3 #=>0.002

1 && nil && 3 # => nilのように左から評価していき、式全体の真偽値が確定した時点で評価を終了する(短絡評価、ショートサーキット)

演算子の優先順位!>&&>||>not>and, or
and, orは制御フローを扱うのに向いている

if文

条件演算子を用いた条件 ? trueの時の実行処理 : falseの時の実行処理

メソッド

  • ?で終わるメソッドは慣習として真偽値を返すメソッド
  • !で終わるメソッドは危険を意味する(破壊的メソッドなど)
  • エンドレスメソッド(1行でメソッドを書ける)
def メソッド名(引数) = 処理
  • エイリアスメソッド: 同じメソッドに複数の名前が付いている

pp

pp 表示したい内容:配列の内容を見やすく整形して出力する

Minitest

Minitestを使ったテストコードの基本系

require'minitest/autorun'
require_relative 'ファイル名'

class SampleTest < Minitest::Test
  def test_sample
    assert_equal A, B
  end
end

検証メソッド

# aがbと等しければパスする
assert_equal b,a 

# aが真であればパスする
assert a

# aが偽であればパスする
refute a

配列

a,b = [1, 2] # =>a = 1, b = 2: 多重代入が可能
a[1, 3]: 2つ目の要素から3つ分の要素を取り出す

配列のメソッド

  • <<: 配列の最後に要素を追加
  • delete_at(要素番号): 要素番号に該当する要素の削除
  • delete(要素): 要素に等しい要素の削除
  • delete_if: 以下のようにブロックの戻り値が真の要素を削除
a.delete_if do |n|
  n.odd?
end
  • values_at(0, 2, 4): 0,2,4の要素を取得する

  • push<< の違いはpushは引数に複数の値を入れることができる

  • delete: 指定した値に一致する要素を削除

  • concat: 破壊的に配列を連結する(非推奨)

  • |、-、&はそれぞれ和集合、差集合、積集合を求めることができる

  • *を前につけて残り全要素を配列として受け取る(途中に使ってもいいa, *b, c

a, *b = 1, 2, 3
p b #=> [2, 3]
  • *だけで残りの配列を無視する
a, * = 1, 2, 3
  • *(splat演算子)を配列の前につけることで配列が展開される
a = [1]
b = [2, 3]
a.push(*b) #=>[1, 2, 3]
[1, *b] #=>[1, 2, 3]
  • 引数の前に*をつけることで可変長引数(rest引数)にできる
defgreet(*names)
  "#{names.join('と')}、こんにちは!"
end
greet('田中さん') #=> "田中さん、こんにちは!"
greet('田中さん','鈴木さん') #=> "田中さんと鈴木さん、こんにちは!"
  • %記法
%w(a b c) #=>["a", "b", "c"]

スペースあり

%w(a\ b c) #=>["a b", "c"]

式展開

a = "A"
%W(#{a} b\n c) #=>["A" "b\n", "c"]

文字列を配列に変換

  • cahrs: 一文字ずつ配列に入れる
Ruby.chars #=> ["R", "u", "b", "y"]
  • split: 区切り文字で配列に入れる
'R,u,b,y'.split #=> ["R", "u", "b", "y"]

デフォルト値

以下のようにデフォルト値を決めることができる。

Array.new(10){ |n| n%3 + 1 } #=> [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]

※第二引数で決めてしまうと、同じものを参照しているので事故が起こりやすい。
ブロックでデフォルト値を決めるように

ブロック

  • map/collect: 各要素に対してブロックを評価した結果を新しい配列にして返す。
    空の配列を用意して、ほかの配列をループ処理した結果を空の配列に詰め込んでいくような処理の大半は、mapメソッドに置き換えることができる
  • select/find_all: 各要素に対してブロックを評価し、その戻り値が真の要素を集めた配列を返す
even_numbers = numbers.select { |n| n.even? }
  • reject: ブロックの戻り値が真になった要素を除外した配列を返す
  • find/detect: ブロックの戻り値が真になった最初の要素を返す
  • sum: 要素の合計を出す
    • sum(初期値)で初期値をオフセットしてタスことができる
    • 先頭の文字列を与えて、文字列を連結することができる
      以下のようにブロックにも使える
[r, g, b].sum('#') do |n|
  n.to_s(16).rjust(2, '0')
end

以下の場合、&とシンボルで書くことができる

  1. ブロックパラメータが1個だけである
  2. ブロックの中で呼び出すメソッドには引数がない
  3. ブロックの中では、ブロックパラメータに対してメソッドを1回呼び出す以外の処理がない
['a', 'b', 'c'].map { |s| s.upcase }
# ↓
['a', 'b', 'c'].map(&:upcase)
  • map.with_index{ |obj, i| "#{i}: #{obj}" }のように、each以外でindexを取れる(引数があると、その数からindexが始まる)

  • 以下のように、複数の要素をブロックに渡すことができる

[[1, 2],[3, 4]].each do | a, b |
  a + b
end #=> [3, 7]
[[1, 2],[3, 4]].each do | (a, b), i |
  a + b + i
end #=> [3, 8]
  • _1, _2など、ブロックパラメータの番号をそのままパラメータとして使える(使うかどうかは要検討)
[[1, 2], [10, 20], [100, 200]].each{ _1+_2 } #=> [3, 30, 300]
  • openメソッドにもブロックは使える

Range

  • .. 以内
  • ... 以上、未満
    ()で囲まないと優先順位が低いので注意
    以下のように配列の要素を範囲指定できる
a = [1, 2, 3, 4, 5]
a[1..3] #=> [2, 3, 4]
  • include?: 基本的に離散値を扱い(Rangeが数値の時は別)、それぞれの要素に対して==で範囲内か判定している(だから、処理が遅い?)。
  • cover?: 連続値を扱い、始点と終端に対して<=>で範囲内か判定する

増減を決める

  • n.upto(m): nからmまで数値を1つずつ増やす
a=[]
10.upto(12) {|n| a<<n}
a #=> [10, 11, 12]
  • n.downto(m): nからmまで数値を1つずつ減らす

  • step(間隔): 値を増やす間隔を指定できる

(1..10).step(2) { |n| numbers << n }
  • 開始値.step(上限値, 一度に増減する大きさ): 開始値から上限値まで増減する大きさを決められる

繰り返し処理

  • whileは1行で書くと
a = []
a <<1 while a.size < 5
  • 必ず、1回は実行する時
begin
  処理
end while false
  • unitl: 条件が偽である間処理を繰り返す

  • for文: 基本的にはeachを使う

for 変数 in 配列やハッシュ
  処理
end
  • loop: 無限ループ

繰り返し処理の制御

  • 入れ子のループ構造の一番外側まで脱出したい
catch タグ do
  処理
  throw タグ 戻り値
end
  • redo`: 現在の繰り返し処理の先頭に戻る
    無限ループを生まないように、上限回数を決めておくこと

シンボル

シンボル特徴:
・表面上は文字列っぽいので、プログラマにとって理解しやすい。
・内部的には整数なので、コンピュータは高速に値を比較できる。
・同じシンボルは同じオブジェクトであるため、メモリの使用効率が良い。
・イミュータブルなので、勝手に値を変えられる心配がない。

ハッシュ

  • delete: 指定したキーに対応する要素を削除

  • メソッドのキーワード引数

def メソッド名(キーワード引数1: デフォルト値1, キーワード引数2: デフォルト値2)

end
  • **をハッシュの前につけるとハッシュの要素を展開できる
    mergeメソッドも同様

  • **引数で任意のキーワードを受け付ける

def メソッド名(key1: value1, key2: value2, **others)

end
  • メソッドへの引数の前に**をつけることで、ハッシュを明示的にキーワード引数に変換する
メソッド名(**hash)
  • ハッシュにデフォルト値を設定する
Hash.new { |hash,key| hash[key] = デフォルト値 }

正規表現

文字クラス: 文字の集合を表す
量指定子: 文字量を指定

文字列 =~ /正規表現/

$~ # MachDataオブジェクトを取得
$& # マッチした部分全体を取得
$1 # 1番目のキャプチャを取得
$+ # 最後のキャプチャ文字を取得
  • {n,m} : 直前の文字が n 個以上、m 個以下
  • [a-z]: aからzのいずれか
  • [AB] : AまたはBが1文字
  • ?: 直前の文字が1個、または無し
  • .: 任意の1文字
  • +: 直前の文字が1個以上(「貪欲」で最長マッチを返す)
  • *: 直前の文字が0個以上(「貪欲」で最長マッチを返す)
  • *? や +? にすると、最短マッチを返す
  • ( ): マッチする部分をキャプチャ(捕捉)and グループ化
  • キャプチャした部分は置換するときに $1 や \1 で参照できる
  • ?:をつけるとキャプチャなし
  • \w: 英単語を構成する文字(半角英数字とアンダースコア)
  • [^AB]: AでもなくBでもない任意の1文字
  • ^: 行頭を表す
  • $: 行末を表す
  • \t: タブ文字を表す
  • \n: 改行文字を表す
  • \s: 空白文字(スペース、タブ文字、改行文字等)を表す
  • ABC|DEF: 「文字列ABCまたは文字列DEF」のOR条件を表す
  • ^: ^文字で行頭の意味になったり、[^ ] で否定の文字クラスの意味になったりする
  • \b: 単語の境界を表す
  • (?=abc): 「abcという文字列の直前の位置」を表す(先読み)
  • (?<=abc): 「abcという文字列の直後の位置」を表す(後読み)
  • (?!abc): 「abcという文字列以外の直前の位置」を表す(否定の先読み)
  • (?<!abc): 「abcという文字列以外の直後の位置」を表す(否定の後読み)
  • キャプチャした文字列は正規表現内でも \1\2 といった連番で参照できる(後方参照)
  • ?*+ といった量指定子は ( ) の後ろに付けることもできる
  • | を使ったOR条件では、各条件内でもメタ文字が使える
  • メタ文字はバックスラッシュ\でエスケープする
  • {n,}{,n} はそれぞれ「直前の文字がn個以上」「n個以下」の意味になる
  • \W\S\D\B はそれぞれ \w\s\d\b の逆の意味になる

=~: マッチすれば真、しなければ偽
!~: 上記と逆

キャプチャに名前をつける

  • (?<名前>): キャプチャに名前をつける
m = /(?<name>)/.match(text)
#以下は全て同じ
m[:name]
m['name']
m[0]
  • キャプチャの名前をローカル変数に割り当てる
/(?<name>)/ =~ text
puts name

ただし、正規表現と対象の文字列を逆にすることはできない
また、正規表現を代入した時にも使えない

便利なStringクラスメソッド

  • scan: 引数で渡した正規表現にマッチする部分を配列に入れて返す
    ()があるとキャプチャを二次元配列として、グループ化する
  • [], slice, slice!: 正規表現を渡すと、文字列から正規表現にマッチした部分を抜き出す
text = 'sample123-4567'
text[/\d{3}-\d{4}/] #=> "123-4567"
text[/(\d{3})-(\d{4})/, 2] #=> "4567"
text[/(?<left_num>\d{3})-(?<right_num>\d{4})/, 'right_num'] #=> "4567"
  • split: マッチした文字列を区切り文字にして文字列を分解し、配列として返す
  • gsub, gsub!: 第1引数の正規表現にマッチした文字列を第2引数の文字列で置換
    置換対応をハッシュで指定可能
text = '1,2-3'
hash = { ',' => ':', '-' => '/' }
text.gsub(/,|-/, hash) #=> "1:2/3"

文字列番号で参照(''で囲むことに注意)

text.gsub(/(\d),(\d)-(\d)/, '\1-\2-\3') #=> "1-2-3"

名前付きキャプチャ

text.gsub(/(?<first>\d),(?<second>\d)-(?<third>\d)/, '\k<first>-\k<third>') #=> "1-3"
text.gsub(/(?<first>\d),(?<second>\d)-(?<third>\d)/) do
  "#{$~[:first]}-#{$~[:third]}"
end
# => "1-3"

クラス

属性(アトリビュート): オブジェクトから取得できる値

継承

継承の関係は「スーパークラスはクラスである」またはその逆が言えるかどうかで考える。

class Product
  attr_reader :name, :price

  def initialize(name, price)
    @name = name
    @price = price
  end
end
  
class DVD <Product
  attr_reader running_time

  def initialize(name, price, running_time)
    super(name, price) # superでもいい
    @running_time = runnning_time
  end
end

クラスメソッドをprivateにするには

class << self

end

定数を外部から参照するにはクラス名::定数名とする。

モジュール

クラスを超えて、同じメソッドを使用する

include メソッド名: クラスでモジュールのメソッドを呼び出す。
ミックスイン: にモジュールをクラスにincludeして機能を追加すること
クラス内でしか使わないのであれば、privateにする

特異メソッド: 特定のオブジェクトにだけ紐付くメソッドのこと
extend: モジュールのメソッドを特異メソッド(クラスメソッド)とする

名前空間

モジュール構文の中にクラス定義を書くと「そのモジュールに属するクラス」という意味になるため、同名のクラスがあっても外側のモジュール名さえ異なっていれば名前の衝突は発生しなくなる
モジュール名::クラス名でモジュールに属するクラスを呼び出す

::クラス名: (左側には何もなし)トップレベルの同一名のクラスを呼び出す

関数や定数を提供

module_function: ミックスインおよび特異メソッド(モジュール関数)として使用可能にする

状態を保持する

インスタンス変数を作成し値を保持する

yieldとProc

yield

yield: メソッド内で渡されたブロックを実行する
引数の前に&をつけることで、ブロックを引数とする(一番最後にすること)。ブロックを実行する場合はcallメソッドを使用する。

def メソッド(&引数)
  引数.call
end

Prco

Procオブジェクト: ブロックなどの処理をオブジェクトとして扱う

パターンマッチ

casein パターン

else

end

asパターン

case {key1: value1, key2: value2}
in {key1: String => var1, key2: 18.. => var2}

in {key1: String, key2: 18.. } => var

end

alternativeパターン

|で繋ぎどれか一つにマッチしたらマッチしたとみなす

自作クラスをマッチさせる

  • arrayパターン
def deconstruct
  [@x, @y]
end
  • hashパターン
def deconstruct_key
  [@x, @y]
end

デバッグ技法

tap: メソッドチェーンを調べる
logger.debug: ログにデバッグ情報を出力する

その他

-W:deprecatedオプションをつけて実行すると警告が出力される

Rake

以下をrake タスク名で実行できる。

task :タスク名 do

end

書き方

  • nilガード
X ||= A

X ||= begin
  代入処理
end

Xがnilまたはfalseなら、AをXに代入

  • 必ず真偽値を返す方法
!!true #=> true
!!1 #=> true
!!false #=> false 
!!nil #=> false
GitHubで編集を提案

Discussion