令和時代の Ruby 基礎文法最速マスター

令和時代の基礎文法最速マスター Advent Calendar 2020 1日目の記事になります。
この Advent Calendar は昔流行っていた 基礎文法最速マスター をまたやりたいなあ…という思いからはじめました。
まだ空きはあるので興味があるひとは参加してもらってもいいですし、別に Advent Calendar である必要もないので書きたいひとがいればどんどん書いていくといいと思います! と、言うことで最初のネタは Ruby になります。
この記事は書かれた時点での最新版である Ruby 2.7.2 に準じる形で書いています。
また、過去の Ruby基礎文法最速マスター はこちら になります。

目次

はじめに

Ruby のインストール

Ruby のインストールは Rails Girls のサイトに詳しく書かれているのでこちらを参考にして Ruby をインストールするのがおすすめです。
こちらのサイトに載っている rbenv というのは複数バージョンの Ruby を管理するツールになっており、rbenv を利用して Ruby をインストールしておくと Ruby のアップデートなどがしやすくなります。
またこちらのサイトには Rails のインストールも含まれていますがそちらはインストールしなくても大丈夫です。

ドキュメント

日本語の Ruby のドキュメントは Ruby リファレンスマニュアル に詳しく記述されています。
Ruby リファレンスマニュアル は各機能やクラス、メソッドのリファレンスが充実しているので任意のクラスにどういうメソッドが定義されているのかを調べたり、メソッドの挙動を確認するときによく利用しています。
また、英語のサイトではありますが The Ruby Reference というサイトもおすすめです。
The Ruby ReferenceRuby の機能や使い方がサンプルコードと一緒に書かれていますのでこちらもざっと読んでみると面白いと思います。

ドキュメントなどでよく書かれる慣習

Ruby では慣習として以下のような記法で説明をすることが多いです。

  • X#hoge -> X クラスの hoge インスタンスメソッド
    • #hoge と省略することもある
  • X.foo -> X クラスの foo クラスメソッド
    • .foo と省略することもある
  • 1 + 2 # => 3 -> 1 + 2 の結果は 3

その他、以下の記事も読んでおくと Ruby の用語が分かるかと思います。

基礎

実行方法

Rubyruby コマンドを使って Ruby のコードを実行します。

ファイルから実行

ruby コマンドにファイルを渡すとそのファイルに書かれた Ruby のコードが実行されます。

$ cat sample.rb
puts "hello, world"
$ ruby sample.rb
hello, world

ruby コマンドに直接コードを渡す

ruby コマンドの -e オプションで任意の Ruby のコードを実行することもできます。

$ ruby -e "puts 'hello, world'"
hello, world
$ ruby -e "puts 1 + 2 * 3"
7

対話環境

irb コマンドで対話的に Ruby のコードを実行する事ができます。
対話環境では入力した式の結果がそのまま出力されます。
また対話環境は exit で終了する事ができます。

$ irb
irb(main):001:0> puts "hello, world"
hello, world
 => nil
irb(main):002:0> 1 + 2
3
 => nil
irb(main):003:0> exit
$

コメント

Ruby# 以降に書かれたテキストがコメントになります。

# これはコメント
puts "hello, world"  # これもコメント

メソッドの呼び出し

Ruby ではメソッドを呼び出す場合に () を省略することができます。

# puts は Ruby ではメソッド呼び出しになる
puts "hello, world"

# () を付けて呼び出すこともできる
puts("hello, world")

出力メソッド

Ruby の出力メソッドは複数あるんですが基本的には putspp の2つだけ覚えておけば大丈夫です。

  • puts は引数を文字列に変換して出力する
  • pp は引数を人間に読みやすい形に変換して出力する
puts [1, 2, 3]
# => 1
#    2
#    3

pp [1, 2, 3]
# => [1, 2, 3]

変数定義

Ruby では変数の宣言はなく、変数に代入する形で変数を定義します。

# これだけでは value という名前のメソッド呼び出しになる
value

# = を使って値を代入することで変数として定義される
value = 42

Ruby では以下のような名前で変数を定義すると特別な意味になります。

value = 42    # => ローカル変数(定義したスコープでのみ参照できる)
@value = 42   # => インスタンス変数(定義したクラスのインスタンス間でのみ参照できる)
@@value = 42  # => クラス変数(定義したクラスでのみ参照できる)
$value = 42   # => グローバル変数(定義されていれば Ruby 全体から参照できる)
FOO = 42      # => 定数

予約語一覧

Ruby では以下の単語が予約語として管理されています。

BEGIN    class    ensure   nil      self     when
END      def      false    not      super    while
alias    defined? for      or       then     yield
and      do       if       redo     true     __LINE__
begin    else     in       rescue   undef    __FILE__
break    elsif    module   retry    unless   __ENCODING__
case     end      next     return   until

基本的に予約語と同名の変数やクラスは定義できません。
ただし、接頭辞に $@ 付いている場合はその限りではありません。

基本構文

条件分岐

if

Ruby では条件分岐を if を使って定義します。

if cond
  puts "cond が真であればこの処理が呼ばれる"
else
  puts "cond が偽であればこの処理が呼ばれる"
  puts "else 節は省略する事ができる"
end

また、Ruby では式の最後に if 文を書くことができます。

puts "cond が真ならここの処理が呼ばれる" if cond

これは『後置 if』と呼ばれています。
この場合は cond が真であれば puts ~ の部分が呼ばれます。
他にも if と逆の意味になる unless 文があります。

unless cond
  puts "cond が偽ならこの処理が呼ばれる"
end
  • 参照:if

case

Ruby では case + when で複数の条件分岐を定義できます。

case value
when 1
  "one"
when 2
  "two"
when 3
  "three"
end
# case when の比較方法は特殊なので以下のような書き方もできる
case "213"
# 正規表現にマッチするかどうか
when /^\d+$/
  "numberes"
# 指定した範囲にマッチするかどうか
when (1..10)
  "in 1 ~ 10"
# Integer のインスタンスかどうか
when Integer
  "is Integer"
end

パターンマッチ

Ruby 2.7 では実験的にパターンマッチ構文が導入されました。
またこれは Ruby 3.0 で正式な機能として導入される予定です。

case obj
# 配列の中身を検証しつつ変数に束縛する
in [first, *, last]
  [first, last]
# ハッシュの中身を検証しつつ変数に束縛する
in { name: String => name, age: Integer => age }
  [name, age]
end

繰り返し

Ruby では繰り返しを forwhile を使って定義します。

while cond
  puts "cond が真の場合にこの処理が呼ばれ続ける"
  #
  cond = get_response
end

for i in [1, 2, 3, 4, 5]
  puts "[1, 2, 3, 4, 5] の配列の要素数分だけここの処理が呼ばれる"
  puts "i に配列の各要素が代入される"
end

Ruby では繰り返し処理を行う場合に後述のイテレーションメソッドを使うことが多く forwhile はあまり使用しません。

メソッド定義

Ruby では def を使ってメソッドを定義します。

# a と b の引数を受け取るメソッドを定義
def plus(a, b)
  a + b
end

pp plus(1, 2)
# => 3

またメソッド内から外にある変数は参照できないので注意しましょう。

value = 42

def plus(a, b)
  # error: undefined local variable or method `value' for main:Object (NameError)
  a + b + value
end

pp plus(1, 2)

Ruby でメソッドを定義する場合は基本的に後述で説明するクラス内で定義します。

デフォルト引数

def func(a, b, c = 3)
  a + b + c
end

pp func(1, 2)
# => 6

可変長引数

def func(*args)
  # args は配列になる
  args
end

pp func(1, 2)
# => [1, 2]

キーワード引数

def func(name:, age:)
  "name:#{name}, age:#{age}"
end

pp func(name: "homu", age: 14)
# => "name:homu, age:14"

ブロック構文

Ruby ではブロック引数と呼ばれる特殊な構文があります。
これはメソッドに対して処理を直接渡すような構文になります。

# & でブロックを受け取る
def calc(&block)
  # call でブロックを評価する
  block.call(1, 2)
end

# || で call で渡した引数を受け取る
result = calc do |a, b|
  a + b
end
pp result # => 3

# do ~ end じゃなくて {} で記述することもできる
result2 = calc { |a, b|
  a - b
}
pp result2  # => -1

また、ブロック内から外の変数を参照する事ができます。

def calc(&block)
  block.call(1, 2)
end

value = 42
result = calc { |a, b|
  a + b + value
}
pp result  # => 45

&渡し

ブロック引数には do end{} 以外にも & で値を渡すことができます。

ary.map(&obj)

これは次のようなコードを同じ意味になります。

# & で渡した値を #to_proc メソッドを経由して呼び出す
ary.map { |it| obj.to_proc.call(it) }

例えば Symbol#to_proc というメソッドが定義されており、これは次のような動作をします。

# Symbol のメソッドを呼び出す
# "homu".upcase と同じ挙動になる
pp :upcase.to_proc.call("homu")
# => "HOMU"

これを利用すると

pp ["homu", "mami", "mado"].map { |str| str.upcase }

というようなコードが

pp ["homu", "mami", "mado"].map(&:upcase)

とショートハンドで書くことができます。

Numbered parameters

Ruby 2.7 から Numbered parameters という機能が入りました。
これはブロックで仮引数を定義しないで _1 _2 _3...という記号でその番号の引数を参照する事ができます。

def calc(&block)
  block.call(1, 2)
end

result = calc { _1 + _2 }
pp result  # => 3

pp (1..10).select { _1.even? }
# => [2, 4, 6, 8, 10]

余談:ブロック引数の結合順位

do ~ end{} は基本的には同じ意味なんですが書き方によってブロック引数を渡すメソッドが異なるので注意する必要があります。

# method1 にブロック引数が渡される
method1 method2 do end
# と同じ意味
method1(method2) do end

# method2 にブロック引数が渡される
method1 method2 {  }
# と同じ意味
method1(method2 {})

余談: pp { name: "homu", age: 14 } はエラーになる

pp { name: "homu", age: 14 } のように pp で Hash を直接出力するとエラーになります。

# syntax error, unexpected ':', expecting '}'
pp { name: "homu", age: 14 }

これは {} がブロックとして認識される為です。
一時変数に代入するか () で囲むことで回避することができます。

pp({ name: "homu", age: 14 })
# => {:name=>"homu", :age=>14}

homu = { name: "homu", age: 14 }
pp homu
# => {:name=>"homu", :age=>14}

余談:ブロックの引数は配列を展開して受け取る

Ruby のブロックの引数は配列を展開して受け取る事があります。

# NOTE: proc は call に渡した値をブロックで受け取る受け取る

# 引数が1つだけの場合は配列をそのまま受け取る
proc { |a|
  p a
  # => [1, 2]
}.call [1, 2]

# 引数が複数ある場合は配列を展開して受け取る
proc { |a, b|
  p a  # => 1
  p b  # => 2
}.call [1, 2]

# これも配列を展開して受け取る
proc { |a,|
  p a  # => 1
}.call [1, 2]

クラス定義

Ruby では class でクラスを定義することができます。

class User
  # .new の引数をこのメソッドで受け取る
  def initialize(name, age)
    # @ が付くとインスタンス変数になる
    @name = name
    @age = age
  end

  # クラス内でメソッドを定義しておくとそのクラスのインスタンスメソッドになる
  def name
    @name
  end

  def age
    @age
  end

  def disp
    # name や age は自身で定義されているメソッドを呼び出す
    # self.name のように self. をつけて呼び出すこともできる
    puts "name:#{name}, age:#{age}"
  end
end

# .new で任意のクラスをインスタンスオブジェクトを生成する
homu = User.new("homu", 14)

pp homu.name  # => "homu"
pp homu.age   # => 14

homu.disp
# => name:homu, age:14

継承

classclass を継承する事ができます。
class は1つのクラスだけ継承する事ができます。

class Super
  def hoge
    "Super#hoge"
  end

  def foo
    "Super#foo"
  end
end

class Sub < Super
  def hoge
    # 親クラスのメソッドを呼び出せる
    foo
  end

  def foo
    # super を使うと同じ名前の親クラスのメソッドを呼ぶ
    super
  end
end

sub = Sub.new

pp sub.hoge
# => "Super#foo"

pp sub.foo
# => "Super#foo"

アクセッサ

class User
  # @name を返す #name と @name に割り当てる #name= メソッドが定義される
  attr_accessor :name
  # @age を返す #age メソッドが定義される
  attr_reader :age

  def initialize(name, age)
    @name = name
    @age = age
  end
end

homu = User.new("homu", 14)
pp homu.name
# => "homu"

homu.name = "homuhomu"
pp homu.name
# => "homuhomu"

pp homu.age
# => 14

# error
homu.age = 15

クラス拡張

Ruby では class を定義した後で再度 class を定義することができます。

class X
  def foo
  end
end

# -------

# 後からクラスを再オープンしてメソッドなどを追加する事ができる
class X
  def hoge
  end
end

これを利用してすでに定義済みのクラスのメソッドの挙動を変更したりすることができます。

メソッドの呼び出し制限

Ruby では publicprivate protected でメソッドの呼び出し制限を行います。

class X
  # デフォルトだと public
  def hoge
    foo
  end

  # 以下のメソッドを private メソッドとして定義する
  # private だと obj.foo のように obj. を付けて呼び出せない
  # foo だけの呼び出しだと呼び出せる
  private

  def foo
    "foo"
  end
end

x = X.new
pp x.hoge   # => "foo"

# error: private method `foo' called for #<X:0x0000564607b7de98> (NoMethodError)
# x. を付けて呼び出すことはできない
x.foo

余談:Object クラス

Ruby では全てのオブジェクトが Object クラスを継承しています。
この Object クラスで汎用的なメソッドが定義されていることで全てのオブジェクトで汎用的なメソッドが使えるようになっています。

モジュール定義

Ruby では module でモジュールを定義します。
定義の仕方はクラスと似ていますが、モジュールは以下のような違いがあります。

  • .newインスタンスオブジェクトを生成できない
  • 継承する事ができない
  • include / prepend で他のクラスやモジュールに取り込める

モジュールは複数のクラスで使用する処理を共通化するための機能になります。

module M
  def foo
    "M#foo"
  end
end

class X
  # M メソッドが X で使えるようになる
  include M
end

x = X.new
pp x.foo
# => "M#foo"

余談:Ruby では全てがオブジェクト

Ruby では全てがオブジェクトです。
1"foo" などといった値はもちろん classmodule で定義したクラスやモジュールも Ruby ではオブジェクトになります。

class X
end

X = Class.new

のように XClass クラスのインスタンスオブジェクトになります。
なので以下のようにクラスのオブジェクトに対してメソッドを呼び出すこともできます。

class X
  def hoge
  end

  def foo
  end
end

# X オブジェクトに対してメソッドを呼ぶ事もできる

# インスタンスメソッド一覧を返す
pp X.instance_methods(false)
# => [:hoge, :foo]

# 継承リスト一覧を返す
pp X.ancestors
# => [X, Object, Kernel, BasicObject]

なので先程使用した attr_accessorattr_readerClass クラスのインスタンスメソッドになります。

余談:Ruby のスコープ

Ruby では classdef 、ブロックなどは独立したスコープを持ちます。
独立したスコープでは外の変数を参照したり、そのスコープで定義した変数は外から参照できません。

value = 42

def func
  # 外で定義されたローカル変数は参照できない
  value

  value2 = 42
end

# func のスコープで定義された変数は参照できない
value2

ただし、ブロック構文の場合は『ブロックの外で定義されたローカル変数は参照すること』が可能です。

value = 42
hoge do
  # value を参照できる
  value

  value2 = 234
end

# ブロック内で定義された変数は外から参照はできない
value2

また、if 文などの制御構文は独立したスコープは持ちません。

value = 42
if true
  # スコープは value と同じになる
  # 参照できる
  value
  value2 = 123
end

# これも参照できる
value2

Refinements

Ruby では特定のコンテキスト内でのみクラス拡張を適用する事ができます。

# String クラスに対して twice メソッドを拡張するような定義
# このモジュールを定義しただけでは String#twice は使えない
module StringEx
  refine String do
    def twice
      self + self
    end
  end
end

# ここでは使えない
# "homu".twice

# クラス拡張を行ったモジュールを using することで
# using したファイルなどでのみ refine した定義が反映される
using StringEx

pp "homu".twice
# => "homuhomu"

演算子

Ruby演算子の多くはメソッドとして定義されています。

1 + 2

1.+(2)

と同じ意味になります。

演算子の定義

演算子はメソッドなのでクラスで再定義することもできます。

class Holder
  attr_reader :value
  def initialize(value)
    @value = value
  end

  # 演算子を定義する
  def +(other)
    Holder.new(value + other.value)
  end
  def -(other)
    Holder.new(value - other.value)
  end
end

one = Holder.new(1)
two = Holder.new(2)

# Holder#+ や #- が呼ばれる
pp one + two
# => #<Holder:0x00005612412cf980 @value=3>
pp one - two
# => #<Holder:0x00005612411ef268 @value=-1>

Ruby で再定義できる演算子は以下になります。

  |  ^  &  <=>  ==  ===  =~  >   >=  <   <=   <<  >>
  +  -  *  /    %   **   ~   +@  -@  []  []=  ` ! != !~

また、再定義できない演算子は以下になります。

  =  ?:  ..  ...  not  &&  and  ||  or  ::

&&|| 演算子

Ruby&&|| は真理値を返さずに右辺や左辺の値をそのまま返します。

# a が真であれば a を返し、そうでなければ b を返す
a || b

# a が真であれば b を返し、そうでなければ nil を返す
a && b

余談:Ruby ではほとんどが文ではなくて式

Ruby では ifdef , class と言った制御構文は文ではなくて式になり、戻り値があります。

# if の結果によって foo か bar を受け取る
result = if cond
           foo
         else
           bar
         end

# def の戻り値は定義したメソッド名のシンボルを返す
name = def hoge
end
pp name # => :hoge

nil

Ruby では値がない表現として nil という値を使用します。
nil は条件式では偽として扱われます。

cond = nil
if cond
  puts "ここの処理は呼ばれない"
end

真理値

Ruby では true を真、 false を偽として扱います。
この2つの値はそれぞれ TrueClassFasleClassインスタンスになります。

余談:Ruby における真と偽

Ruby では nilfalse は偽となり、それ以外の値は全て真になります。
C言語などでは 0 を偽として扱ったりするのですが Ruby では 0 という値は真になるので注意しましょう。

if 0
  puts "ここの処理が呼ばれる"
end

数値

数値の表現

整数や浮動小数点、有理数などが定義できます。

42       # 整数
-42      # 符号付き整数
3.14     # 浮動小数点
0xff     # 16進数
0b1011   # 2進数
0o377    # 8進数
42r      # 有理数
42i      # 複素数
42ri     # 虚数部が有理数の複素数

また数値には _ を含める事ができ、Ruby ではこの _ は無視されて実行されます。

# これは両方共同じ値になる
123456789
123_456_789

四則演算

value = 1 + 2
value = 1 - 2
value = 1 * 2
value = 1 / 2
value = 5 % 2

インクリメントとデクリメント

Ruby にはインクリメントとデクリメントを表現する演算子はありません。
インクリメントやデクリメントを表現したい場合は +=-= を使用します。

# value = value + 1 の略
value += 1

# value = value - 1 の略
value -= 1

文字列

Ruby では文字列を "" または '' で囲って定義します。
"" ではバックスラッシュ記法と式展開が有効になり、 '' では有効になりません。

puts "homu"  # => homu
# #{式} で文字列の中に式を埋め込むことができる
puts "1 + 2 = #{1 + 2}"  #=> 1 + 2 = 3
# \t など特殊文字を利用できる
puts "homu\tmami"  #=> homu mami

# 式展開や特殊文字は適用されない
puts '1 + 2 = #{1 + 2}'  # => 1 + 2 = #{1 + 2}
puts 'homu\tmami'        # => homu\tmami

ヒアドキュメント

Ruby では以下のような形式でヒアドキュメントを定義する事ができます。

# EOS から次の EOS が出現するまでを文字列として定義する
# EOS は任意の文字列を指定できる
puts <<~EOS
homu
mami
mado
#{1 + 2}
EOS
# => homu
#    mami
#    mado
#    3

また、メソッドに複数の引数に混ぜて渡す場合は以下のような書き方になります。

# <<~EOS までを引数として渡す
method(a, <<~EOS, other)
homu
mami
EOS

文字列の結合

pp "homu" + "mami"
# => "homumami"

文字列を n回繰り返して結合する

pp "homu" * 4
# => "homuhomuhomuhomu"

文字列の分割

pp "homu,mami,mado".split(",")
# => ["homu", "mami", "mado"]

文字数

pp "homuhomu".length
# => 8
# #size でも OK
pp "mamimami".size
# => 8

文字の切り出し

# 3文字目から 5文字を取得する
pp "homumami"[2, 4]
# => "muma"

# 3文字目から 7文字目までを取得する
pp "homumami"[2..6]
# => "mumam"

# 3文字目以降全てを取得する
pp "homumami"[2..]
# => "mumami"

# 6文字目以前全てを取得する
pp "homumami"[..5]
# => "homuma"

文字の検索

# : の文字の位置を返す
pp "name : homu".index(/:/)
# => 5

文字列の比較

# 文字列同士の比較
pp "homu" == "homu"   # => true
pp "homu" == "mami"   # => false

# 正規表現での比較
# NOTE: =~ は真理値ではなくてマッチした位置を返すので注意
pp "homu" =~ /^h/   # => 0
pp "homu" =~ /m/    # => 2

# マッチしなかった場合は nil を返す
pp "homu" =~ /\d/   # => nil

文字の置換

# 第一引数にマッチした値を第二引数に置き換える
pp "homuhomu".gsub(/h/, "H")
# => "HomuHomu"

シンボル

Ruby では先頭に : を付けたテキストをシンボルとして定義します。

pp :homu
# => :homu

文字列と似ていますがシンボルは常に一意のオブジェクトです。

# 文字列はオブジェクトid が異なる
pp "homu".object_id   # => 60
pp "homu".object_id   # => 80

# シンボルは同じオブジェクトid になる
pp :homu.object_id    # => 1023708
pp :homu.object_id    # => 1023708

文字列とシンボルは似ているようで全く異なるオブジェクトになるので注意する必要があります( 0"0" ぐらい違う。

文字列へ変換

pp :homu.to_s
# => "homu"

正規表現

Ruby では //正規表現を定義します。

numbers = /\d+/
pp numbers.match? "123"      # => true
pp numbers.match? "homu"     # => false
pp numbers.match? "mami123"  # => true

# =~ はマッチした位置を返す
pp numbers =~ "123"      # => 0
pp numbers =~ "homu"     # => nil
pp numbers =~ "mami123"  # => 4

配列

Ruby では配列を [] で囲って定義します。

pp [1, 2, 3]
# => [1, 2, 3]

要素の参照と代入

ary = [1, 2, 3]
# n 番目の要素にアクセス
pp ary[1]  # => 2

# 要素に代入する
pp ary[1] = 42
pp ary     # => [1, 42, 3]

# 存在しない要素にアクセスすると nil が返ってくる
pp ary[100]  # => nil

配列の個数

# 全要素数をカウント
pp [1, 2, 3, 4, 5].size
# 指定した要素数をカウント
pp [3, 2, 1, 2, 1, 1].count(1)
# => 3

配列の操作

ary = [1, 2, 3, 4, 5]
# 先頭に追加
ary.unshift(0)
# 末尾に追加
ary.push(6)
ary << 7
pp ary
# => [0, 1, 2, 3, 4, 5, 6, 7]

# 先頭を取り出す
pp ary.shift
# => 0
# 末尾を取り出す
pp ary.pop
# => 7

pp ary
# => [1, 2, 3, 4, 5, 6]

配列の結合

pp [1, 2, 3] + [4, 5]
# => [1, 2, 3, 4, 5]
pp [1, 2, 3].concat [4, 5]
# => [1, 2, 3, 4, 5]

配列を n回繰り返して結合する

pp [1, 2, 3] * 3
# => [1, 2, 3, 1, 2, 3, 1, 2, 3]

配列に要素が存在するかどうか

ary = ["homu", "mami", "mado"]
pp ary.include? "homu"   # => true
pp ary.include? "saya"   # => false

配列の最小値、最大値

# 最小値
pp [3, 2, 1, 4, 5, 0].min     # => 0
# 最大値
pp [3, 2, 1, 4, 5, 0].max     # => 5
# 最小値と最大値
pp [3, 2, 1, 4, 5, 0].minmax  # => [0, 5]

配列の展開

配列値の先頭に * を付けると配列を展開してメソッドの引数に渡します。

def sum(a, b, c)
  a + b + c
end

ary = [1, 2, 3]
pp sum(*ary)
# => 6

ハッシュ

Ruby ではハッシュ(連想配列)は {} で囲って定義します。

# キーをシンボルとしたハッシュを定義
homu = { name: "homu", age: 14 }
pp homu
# => {:name=>"homu", :age=>14}

# 以下のように => を使うと好きな値をキーにする事もできる
mami = { "name" => "mami", :age => 14, 1 => "one" }
pp mami
# => {"name"=>"mami", :age=>14, 1=>"one"}

要素の参照と代入

homu = { name: "homu", age: 14 }
# 要素の取得
pp homu[:name]  # => "homu"
pp homu[:age]   # => 14
# 存在しない場合は nil を返す
pp homu[:id]    # => nil

# 要素の変更
homu[:age] = 15
# 要素の追加
homu[:id] = 1
pp homu
# => {:name=>"homu", :age=>15, :id=>1}

要素を取得する際にデフォルト値を指定する

homu = { name: "homu", age: 14 }
# キーが存在しなければ第二引数を返す
pp homu.fetch(:name, "default")  # => "homu"
pp homu.fetch(:id, "default")    # => "default"

キーの有無を確認

homu = { name: "homu", age: 14 }
pp homu.include? :name  # => true
pp homu.include? :id    # => false

要素の有無を確認

homu = { name: "homu", age: 14 }
pp homu.value? "homu"  # => true
pp homu.value? 123     # => false

キーの一覧を取得

homu = { name: "homu", age: 14 }
pp homu.keys
# => [:name, :age]

値の一覧を取得

homu = { name: "homu", age: 14 }
pp homu.values
# => ["homu", 14]

キーの要素を削除

homu = { name: "homu", age: 14 }

# キーの要素を削除する
# 削除した値を返す
pp homu.delete(:name)
# => "homu"

pp homu
# => {:age=>14}

ハッシュの結合

homu = { name: "homu", age: 14 }
mami = { id: 14, name: "mami" }
# homu を mami で上書きする
pp homu.merge(mami)
# => {:name=>"mami", :age=>14, :id=>14}

余談:文字列とシンボルは異なるキーになるので注意

文字列とシンボルは似ているんですが異なるキーの値になります。
Ruby では扱うデータによって『キーがシンボルのハッシュ』と『キーが文字列のハッシュ』の両方のハッシュを扱うことがあるので注意しましょう。
たまにキーがシンボルだと思ってたら実は文字列だった、ということがあります…。

hash = { "one" => 1, one: 11, "two" => 2, three: 3 }
pp hash["one"]     # => 1
pp hash[:one]      # => 11
pp hash["two"]     # => 2
pp hash[:two]      # => nil
pp hash["three"]   # => nil
pp hash[:three]    # => nil

範囲オブジェクト

Ruby では範囲オブジェクトを ..... で定義します。

# 1 ~ 10 までの範囲を定義
# 10 を含める
pp (1..10)      # => 1..10
# 10 を含めない
pp (1...10)     # => 1...10

# "a" ~ "z" までの範囲を定義
pp ("a".."z")   # => "a".."z"

範囲に含まれているか判定する

pp (1..10).cover? 3   # => true
pp (1..10).cover? 10  # => true
pp (1..10).cover? -1  # => false

無限範囲

Ruby では範囲を定義する際に先端や終端を省略することで無限範囲として定義されます。

# 終端無限
pp (1..).cover? 3   # => true
pp (1..).cover? 10  # => true
pp (1..).cover? -1  # => false
# 先端無限
pp (..1).cover? 3   # => false
pp (..1).cover? 10  # => false
pp (..1).cover? -1  # => true

繰り返し処理

Ruby では Enumerable というモジュールに汎用的な繰り返しメソッドが定義されています。
配列やハッシュではこの Enumerable が組み込まれており繰り返し処理を行う場合はここで定義されているメソッドを利用する事が一般的です。
それぞれの繰り返しメソッドは基本的にブロック引数を受け取り、そのブロックで繰り返しの処理を記述するような使い方になりなります。

要素を繰り返す

# 要素数分ブロックを呼び出す
# 要素はその要素になる
[3, 2, 5, 1, 4].each { |it|
  puts it + it
}
# => 6
#    4
#    10
#    2
#    8

# ハッシュの場合は [key, value] の配列で受け取る
{ name: "homu", age: 14 }.each { |it|
  pp it
}
# => [:name, "homu"]
#    [:age, 14]

# key と value を分解して受け取る事もできる
{ name: "homu", age: 14 }.each { |key, value|
  puts "#{key} = #{value}"
}
# => name = homu
#    age = 14

要素を n個で分割して繰り返す

(1..10).each_slice(3) { |it|
  pp it
}
# => [1, 2, 3]
#    [4, 5, 6]
#    [7, 8, 9]
#    [10]

要素を n要素ずつで区切り繰り返す

(1..10).each_cons(3) { |it|
  pp it
}
# => [1, 2, 3]
#    [2, 3, 4]
#    [3, 4, 5]
#    [4, 5, 6]
#    [5, 6, 7]
#    [6, 7, 8]
#    [7, 8, 9]
#    [8, 9, 10]

逆順に繰り返す

(1..5).reverse_each { |it|
  puts it + it
}
# => 10
#    8
#    6
#    4
#    2

逆順の配列に変換する

pp (1..5).reverse_each.to_a
# => [5, 4, 3, 2, 1]

引数の要素があるかどうかを判定する

pp [3, 2, 5, 1, 4].include?(3)       # => true
pp [3, 2, 5, 1, 4].include?(10)      # => fasle
pp [3, 2, 5, 1, 4].include?("homu")  # => fasle

条件に合う要素を検索

# 最初に見つかった偶数の要素を返す
pp [3, 2, 5, 1, 4].find { |it| it.even? }
# => 2

pp ({ one: 1, two: 2, three: 3 }).find { |key, value| value.even? }
# => [:two, 2]

条件に合う要素を返す

# 奇数のみを絞り込む
pp (1..10).select { |it| it.odd? }
# => [1, 3, 5, 7, 9]

条件に合う要素を取り除く

# 奇数を除く
pp (1..10).reject { |it| it.odd? }
# => [2, 4, 6, 8, 10]

条件に満たす要素とそうでない要素で分ける

pp (1..10).partition { |it| it.even? }
# => [[2, 4, 6, 8, 10], [1, 3, 5, 7, 9]]

ブロックの戻り値ごとに要素を分ける

# ブロックの戻り値がキーとなり、その値を配列にする
pp (1..10).group_by { |it| it % 3 }
# => {1=>[1, 4, 7, 10], 2=>[2, 5, 8], 0=>[3, 6, 9]}

要素を変換

pp (1..10).map { |it| it + it }
# => [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# ハッシュも配列を返す
pp ({ one: 1, two: 2, three: 3 }).map { |key, value| [key.to_s, value] }
# => [["one", 1], ["two", 2], ["three", 3]]

絞り込みつつ要素を変換する

pp (1..10).filter_map { |it|
  it + it if it.even?
}
# => [4, 8, 12, 16, 20]

並び替え

pp ["mado", "homu", "mami"].sort
# => ["homu", "mado", "mami"]

# 任意の要素を参照して並び替える
pp ["mado", "homuhomu", "an"].sort_by { |it| it.length }
# => ["an", "mado", "homuhomu"]

条件に合う要素を数える

pp (1..10).count { |it| it.even? }
# => 5

条件に合う要素があるか判定する

pp ["homu", "mami", "mado"].any? { |it| /^m/ =~ it }
# => true

畳み込み

# (((1 + 2) + 3) + 4...)
pp (1..10).inject { |it, sum|
  sum + it
}
# => 55

インデックス値を付属して繰り返す

["homu", "mami", "mado"].each_with_index { |it, index|
  puts "#{index} : #{it}"
}
# => 0 : homu
#    1 : mami
#    2 : mado

# チェーンして他のメソッドで受け取る
pp ["homu", "mami", "mado"].map.with_index { |it, index|
 "#{index} : #{it}"
}
# => ["0 : homu", "1 : mami", "2 : mado"]

pp ["homu", "mami", "mado", "an", "saya"].select.with_index { |it, index|
  index.even?
}
# => ["homu", "mado", "saya"]

余談: 0 〜 9 を繰り返す

Ruby では 0 ~ 9 を繰り返す書き方が何種類かあるので紹介します。

# Range#each
(0..9).each { |i|
  puts i
}
# Integer#upto
0.upto(9) { |it|
  puts it
}
# Integer#times
10.times { |it|
  puts it
}

パーセント記法

Ruby ではパーセント記法という特別な記法があります。
パーセント記法は % から始まり任意の対応する括弧で値を記述する事ができます。

# スペース区切りの文字列の配列を定義する
pp %w(homu mami mado)
# => ["homu", "mami", "mado"]

# () は {} や []、"" など好きな記号で代替できる
pp %w{homu mami mado}
pp %w[homu mami mado]
pp %w"homu mami mado"
pp %w!homu mami mado!
  • %(テキスト) : ダブルクォート文字列
  • %Q(テキスト) : 同上
  • %q(テキスト) : シングルクォート文字列
  • %x(テキスト) : コマンド出力
  • %r(テキスト) : 正規表現
  • %w(テキスト) : 要素が文字列の配列(空白区切り)
  • %W(テキスト) : 要素が文字列の配列(空白区切り)。式展開、バックスラッシュ記法が有効
  • %s(テキスト) : シンボル。式展開、バックスラッシュ記法は無効
  • %i(テキスト) : 要素がシンボルの配列(空白区切り)
  • %I(テキスト) : 要素がシンボルの配列(空白区切り)。式展開、バックスラッシュ記法が有効

  • 参照:%記法

ファイル

読み込み

# ファイルの中身を文字列で全て読み込んでくる
file = File.read("hoge.rb")
pp file
# => "# ファイルの中身を全てテキスト形式で読み込んでくる\n" +
#    "file = File.read(\"hoge.rb\")\n" +
#    "pp file\n"

書き込み

# ファイルを開いて書き込む
File.open("foo.txt", "wb") do |file|
  file.write "homu\n"
  file.write "mami\n"
  file.write "mado\n"
end

pp File.read("foo.txt")
# =+> "homu\n" + "mami\n" + "mado\n"

http/ftp

Ruby では library open-uri ライブラリを使用して http/ftp に簡単にアクセスできます。

require 'open-uri'

URI.open("http://www.ruby-lang.org/") {|file|
  # 1行ずつ読み込んでくる
  file.each_line { |line|
    puts line
  }
}
__END__
output:
<!DOCTYPE html>
<html>
  <head>
〜 省略 〜
  </head>
  <body>
    <p><a href="/en/">Click here</a> to be redirected.</p>
  </body>
</html>

その他:よく使うライブラリ

組み込みライブラリ

標準ライブラリ

Ruby における慣習

NOTE: あくまでも慣習でありその限りではありません

! 付きメソッド

Ruby ではメソッドを定義するときにメソッド名の最後に ? を付けて定義する事ができます。
! を付けてもメソッドの挙動は変わらないんですが、Ruby の慣習として『何かびっくりする事が起きる』場合に ! を付けてメソッドを定義することが多いです。
例えば副作用があるメソッドやメソッドの内部で失敗したら例外が発生するような場合にメソッド名に ! を付けたりします。

ary = [1, 2, 3]

# ary 自身をブロックの戻り値で書き換える
ary.map! { |it| it + it }

p ary
# => [2, 4, 6]

? 付きメソッド

Ruby ではメソッドを定義するときにメソッド名の最後に ? を付けて定義する事ができます。
全てではありませんが ? 付きメソッドは真理値を返す事が多いです。
また、Ruby で真理値を返すメソッドを定義する倍は #has_xxx#is_yyy という名前ではなくて #xxx?#yyy? と書くことが多いです。

# obj が nil かどうかを判定
# true / false を返す
obj.nil?

_ 変数

Ruby では使用しない変数を _ という名前で定義(受け取る)事があります。

# 第二引数は使わないので _ で受け取る
{ one: 1, two: 2, three: 3 }.map { |key, _| key.to_s }

||=インスタンス変数の初期化

Ruby では @value ||= 初期値 のようにして初期化する事が多いです

class X
  def insert(value)
    # [] で初期化する
    # @value = @value || [] # これと同じ意味
    @value ||= []
    @value << value
    @value
  end
end

x = X.new
pp x.insert(1)   # => [1]
pp x.insert(2)   # => [1, 2]
pp x.insert(3)   # => [1, 2, 3]

初心者向けのおすすめ書籍

おわりに

基礎文法という割にはかなりガッツリ書かれた内容になりました。
もしわかりづらい部分やこういう箇所も書いてほしい、みたいな意見があればコメントもらえると助かります。
何かあれば随時補足していきたいなあ、と思います。
この記事を読んで Ruby に触れる方がおられるとうれしいです。
Ruby はいいぞ〜。