令和時代の Ruby 基礎文法最速マスター
令和時代の基礎文法最速マスター Advent Calendar 2020 1日目の記事になります。
この Advent Calendar は昔流行っていた 基礎文法最速マスター をまたやりたいなあ…という思いからはじめました。
まだ空きはあるので興味があるひとは参加してもらってもいいですし、別に Advent Calendar である必要もないので書きたいひとがいればどんどん書いていくといいと思います!
と、言うことで最初のネタは Ruby になります。
この記事は書かれた時点での最新版である Ruby 2.7.2 に準じる形で書いています。
また、過去の Ruby基礎文法最速マスター はこちら になります。
目次
- 目次
- はじめに
- 基礎
- 基本構文
- nil
- 真理値
- 数値
- 文字列
- シンボル
- 正規表現
- 配列
- ハッシュ
- 範囲オブジェクト
- 繰り返し処理
- パーセント記法
- ファイル
- http/ftp
- その他:よく使うライブラリ
- Ruby における慣習
- 初心者向けのおすすめ書籍
- おわりに
はじめに
Ruby のインストール
Ruby のインストールは Rails Girls のサイトに詳しく書かれているのでこちらを参考にして Ruby をインストールするのがおすすめです。
こちらのサイトに載っている rbenv
というのは複数バージョンの Ruby を管理するツールになっており、rbenv
を利用して Ruby をインストールしておくと Ruby のアップデートなどがしやすくなります。
またこちらのサイトには Rails のインストールも含まれていますがそちらはインストールしなくても大丈夫です。
ドキュメント
日本語の Ruby のドキュメントは Ruby リファレンスマニュアル に詳しく記述されています。
Ruby リファレンスマニュアル は各機能やクラス、メソッドのリファレンスが充実しているので任意のクラスにどういうメソッドが定義されているのかを調べたり、メソッドの挙動を確認するときによく利用しています。
また、英語のサイトではありますが The Ruby Reference というサイトもおすすめです。
The Ruby Reference は Ruby の機能や使い方がサンプルコードと一緒に書かれていますのでこちらもざっと読んでみると面白いと思います。
ドキュメントなどでよく書かれる慣習
Ruby では慣習として以下のような記法で説明をすることが多いです。
X#hoge
->X
クラスのhoge
インスタンスメソッド#hoge
と省略することもある
X.foo
->X
クラスのfoo
クラスメソッド.foo
と省略することもある
1 + 2 # => 3
->1 + 2
の結果は3
その他、以下の記事も読んでおくと Ruby の用語が分かるかと思います。
基礎
実行方法
Ruby は ruby
コマンドを使って 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 の出力メソッドは複数あるんですが基本的には puts
と pp
の2つだけ覚えておけば大丈夫です。
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 # => 定数
予約語一覧
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
- 参照:case
パターンマッチ
Ruby 2.7 では実験的にパターンマッチ構文が導入されました。
またこれは Ruby 3.0 で正式な機能として導入される予定です。
case obj # 配列の中身を検証しつつ変数に束縛する in [first, *, last] [first, last] # ハッシュの中身を検証しつつ変数に束縛する in { name: String => name, age: Integer => age } [name, age] end
繰り返し
Ruby では繰り返しを for
や while
を使って定義します。
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 では繰り返し処理を行う場合に後述のイテレーションメソッドを使うことが多く for
や while
はあまり使用しません。
メソッド定義
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
継承
class
は class
を継承する事ができます。
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 では public
や private
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
クラスで汎用的なメソッドが定義されていることで全てのオブジェクトで汎用的なメソッドが使えるようになっています。
- 参照: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"
などといった値はもちろん class
や module
で定義したクラスやモジュールも Ruby ではオブジェクトになります。
class X end
は
X = Class.new
のように X
は Class
クラスのインスタンスオブジェクトになります。
なので以下のようにクラスのオブジェクトに対してメソッドを呼び出すこともできます。
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_accessor
や attr_reader
は Class
クラスのインスタンスメソッドになります。
余談:Ruby のスコープ
Ruby では class
や def
、ブロックなどは独立したスコープを持ちます。
独立したスコープでは外の変数を参照したり、そのスコープで定義した変数は外から参照できません。
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"
- 参照
演算子
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>
| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ +@ -@ [] []= ` ! != !~
また、再定義できない演算子は以下になります。
= ?: .. ... not && and || or ::
&&
や ||
演算子
Ruby の &&
や ||
は真理値を返さずに右辺や左辺の値をそのまま返します。
# a が真であれば a を返し、そうでなければ b を返す a || b # a が真であれば b を返し、そうでなければ nil を返す a && b
余談:Ruby ではほとんどが文ではなくて式
Ruby では if
や def
, 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
- 参照:NilClass
真理値
Ruby では true
を真、 false
を偽として扱います。
この2つの値はそれぞれ TrueClass
と FasleClass
のインスタンスになります。
余談:Ruby における真と偽
Ruby では nil
と false
は偽となり、それ以外の値は全て真になります。
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"
- 参照:String#*
文字列の分割
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"
- 参照:String#[]
文字の検索
# : の文字の位置を返す pp "name : homu".index(/:/) # => 5
- 参照:String#index
文字列の比較
# 文字列同士の比較 pp "homu" == "homu" # => true pp "homu" == "mami" # => false # 正規表現での比較 # NOTE: =~ は真理値ではなくてマッチした位置を返すので注意 pp "homu" =~ /^h/ # => 0 pp "homu" =~ /m/ # => 2 # マッチしなかった場合は nil を返す pp "homu" =~ /\d/ # => nil
- 参照:String#=~
文字の置換
# 第一引数にマッチした値を第二引数に置き換える pp "homuhomu".gsub(/h/, "H") # => "HomuHomu"
- 参照:String#gsub
シンボル
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"
正規表現
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]
- 参照:Array#*
配列に要素が存在するかどうか
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
が組み込まれており繰り返し処理を行う場合はここで定義されているメソッドを利用する事が一般的です。
それぞれの繰り返しメソッドは基本的にブロック引数を受け取り、そのブロックで繰り返しの処理を記述するような使い方になりなります。
- 参照: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"
- 参照:File
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>
その他:よく使うライブラリ
組み込みライブラリ
標準ライブラリ
- library optparse:コマンドラインオプションのパーサ
- library delegate:メソッドの委譲を行うライブラリ
- library csv csv を扱うライブラリ
- library json json を扱うライブラリ
- library yaml yaml を扱うライブラリ
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 はいいぞ〜。