Ruby

Ruby入門したときの勉強ノート 〜初心者がまず覚えるべきことまとめ〜

 この記事はRuby入門者が0から勉強を始め、挫折したりど忘れしたりしないよう学んだことの全てを体系化する目的で書いたもの。理解できてない状態で勉強しながらリアルタイムで更新しているので、初心者が理解するのに最適化された目次となっているはず。

 筆者はプログラミングが苦手なタイプで、以前C言語でテトリスを作ろうとして、着地してもいつまでもブロックが回り続けるゲームを作って挫折したレベル。ただ、Webに関してはHTML、CSS、PHPを使ってまともなCMSを構築した経験はある。このたび、ロリポップ!レンタルサーバーに静的HTMLで構築していたHPを、GithubJekyllでシンプルに作り変えるためにRubyを勉強し始めたところ。

参考文献
Progate
『プロを目指す人のためのRuby入門』の目次

目次

基本的な文法

コメントアウト

# コメントアウト

文字列の出力

print "文字列"         # 文字列(改行なし)
puts "文字列"          # 文字列(改行あり)
p "文字列"             # "文字列"(データの形のまま出力)

四則演算

puts 3 + 2          # 5
puts 3 - 2          # 1
puts 3 * 2          # 6
puts 3 / 2          # 1(小数点以下切り捨て)
puts 3.0 / 2.0      # 1.5
puts 3 % 2          # 1(あまりの値)

文字列を連結

puts "文字列" + "連結"          # 文字列連結

変数の定義・代入・出力

 とりあえず下記の例では、変数は英語でvariableということで略して変数名をvar_nameとしている。実際に使うときは、変数に格納されるデータの内容に適した変数名を定義すればいい。

  • 名前ならname
  • 日付ならdate
  • 数字ならnumberなど
var_name = "文字列"          # 変数var_nameに "文字列" という文字列を代入
var_name = 123              # 変数var_nameに 123 という数値を代入

 変数に中の数で四則演算を行い、その結果を同じ変数にそのまま代入する省略形。四則演算子と等号を繋げて書くだけ。

var_name = 1

var_name = var_name + 1         # 変数に、変数+1の結果を代入
puts var_name                   # 2

var_name += 1                   # 変数を+1する
puts var_name                   # 2(同じ結果になる)

 変数を活用して文字列の書き換えを自動化する例。文字列の中間に変数の値を出力するには#{}で変数名を囲って挿入する。また、文字列は"'で囲うことで認識されるが、#{}'で囲うと変数としてではなく、データの型をそのまま出力してしまうので注意。

var_name = "文字列"

puts "変数と#{var_name}を連結"          # 変数と文字列を連結
puts '変数と#{var_name}を連結'          # 変数と#{var_name}を連結

条件分岐

比較演算子

 条件分岐の処理を行うためには、ある条件にしたがって値と値を比較して真偽を判定する必要がある。下記の6つの比較演算子があればあらゆる比較条件を再現できるはず。

a == b          # aとbが等しい場合は
a != b          # aとbが等しくない場合は
a < b           # aがb未満なら
a <= b          # aがb以下なら
a > b           # aがbを超えるなら
a >= b          # aがb以上なら

AND・OR

 比較条件が複数ある場合は、その条件を列挙すればいい。

a && b                   # aかつbなら
a || b                   # aもしくはbなら
a == 1 || a >= 5         # aが1と等しい、もしくは5以上なら

if文

 if文の修飾子は3つある。if文を意訳すると「もし条件1ならこうして、条件2ならこうして、条件3ならこうして、それ以外ならこうする」という感じ。条件を増やしたいならelsifで条件を追加する。

if var_name > 2                            # var_nameが2を超えるなら
  puts "多い"                               # "多い"と出力

elsif var_name <= 2 && var_name >= 1       # var_nameが2以下1以上なら
  puts "少ない"                             # "少ない"と出力

else                                       # 該当なし(1未満)なら
  puts "ない"                               # "ない"と出力
end

配列

配列の定義

 配列(array)はデータの集合体を扱うときに定義する。配列の中の要素には、1番目の要素から順に[0]から始まる通し番号がアドレスとして割り振られている。配列を構成する要素は必要な数だけ用意する。例えば、ユーザー登録なら["user1", "user2", "user3"]など?

array_name = ["index1", "index2", "index3"]
puts array_name                              # index1
                                             # index2
                                             # index3

puts array_name[0]                           # index1

ハッシュ

 ハッシュとは、キーとバリューというペアの値のことをいう。ハッシュの定義は{"key" => "value"}と書く。ハッシュの用途をわかりやすく言うと、キーは何のデータかを示すラベルのようなもので、バリューは値そのものという感じ。例えば、{"apple" => 130, "orange" => 50}とか。

var_name = {"key1" => "value1", "key2" => "value2"}
puts var_name                                         # {"key1" => "value1", "key2" => "value2"}
puts var_name["key1"]                                 # value2

 ハッシュの内容を上書きするには、任意のキーに上書きする文字列や値を代入すればいい。普通の変数と同じように。

var_name = {"key1" => "value1", "key2" => "value2"}
var_name["key2"] = "v2"                               # key2の文字列を"value2"から"v2"に上書き
puts var_name                                         # {"key1" => "value1", "key2" => "v2"}

 ハッシュに新規要素を追加するには、新たなキーの名前を定義してバリューを代入する。

var_name = {"key1" => "value1", "key2" => "value2"}
var_name["key3"] = "value3"
puts var_name                 #{"key1" => "value1", "key2" => "value2", "key3" => "value3}

ハッシュの配列

 配列の中の値をハッシュにすると、よりデータ管理がやりやすくなる。配列がデータの集合体だとすると、ハッシュはデータの中身の構造を決めるものと言える。

rows = [                                   # 以降は配列名をarray_nameではなくrowsとする
  {key1:"value1", key2:"value2"},          # 配列を改行するときは「,」を忘れずに
  {key1:"value11", key2:"value22"}
]

puts rows[0]                               # {key1:"value1", key2:"value2"}

row = rows[0]                              # 配列rowsの1番目の要素を変数rowに代入
puts row[:key1]                            # value1          

puts rows[0][:key1]                        # value1

 上の方で配列の例として["user1", "user2", "user3"]というユーザー名簿のようなものを挙げたが、ハッシュと組み合わせればユーザー登録情報のような管理ができるようになる。

# ユーザー登録情報の、名前、年齢、Emailを格納した例

rows = [
  {"name" => "山田", "age" => 18, "email" => "xxx@xxxx"},
  {"name" => "田中", "age" => 23, "email" => "xxxx@xxxxx"},
  {"name" => "山中", "age" => 20, "email" => "xx@xxx"}
]

シンボルでハッシュを省略表記

 シンボルとは、ハッシュにおけるキーの部分の文字列を"'で囲む代わりに、接頭語に:を付ける省略法。例えば"key1"というのを:key1と省略できる。効率がいいのでゆくゆくはこっちで慣れた方が良さそう。シンボルで省略したものも、普通のハッシュと同じように定義と出力ができる。

var_name = {"key1" => "value1", "key2" => "value2"}          # ハッシュを定義
puts var_name["key1"]                                        # value1


var_name = {:key1 => "value1", :key2 => "value2"}            # ハッシュをシンボルで表記して定義
puts var_name[:key1]                                         # value1(結果は同じ)

eachメソッドで繰り返し処理

 each文は配列のようなデータの集合を一通り読み込む処理。任意に範囲を指定して読み込むfor文との違いは、each文は配列データ全てに対して処理を行うのでデータ検索などに使えそう。

rows = ["row1", "row2", "row3"]                #配列名は複数形rows
rows.each do|row|                              #each文内で定義する変数は配列名の単数形row
  puts row                                     #row1
                                               #row2
                                               #row3
end

 each文に渡す配列データがハッシュのときは、キーを指定すれば対応するバリューを読み込む処理になる。

rows = [
  {key1:"value1", key2:"value2"},
  {key1:"value11", key2:"value22"}
]


rows.each do|row|
  puts row[:key1]             #value1
                              #value11
end

nilは空オブジェクト

メソッド

メソッドの定義と呼び出し

メソッドとは、任意の一連の処理を一言で呼び出せるようにまとめたもの。メソッドはdef(定義を意味するdefineの略)を使って定義する。関数とも呼ばれるそうな。オブジェクト指向の基本の一つ。

def method_name
  print "メソッドに組み込む"
  puts "一連の処理"
end

method_name                  # メソッドに組み込む一連の処理   と出力される

引数

 引数とは、メソッドの内部で行う処理に任意の値を渡すための変数のこと。arg_nameとはargument(引数)の略。

def method_name(arg_name)
  puts "みなさん#{arg_name}"
end

method_name("こんにちは")                  # みなさんこんにちは
method_name("さようなら")                   # みなさんさようなら

 メソッドに渡す引数の個数に制限はなく、,で区切って必要なだけ定義できる。呼び出すときは対応した順番通りに引数を入力する。

def method_name(arg1, arg2)
  puts "みなさん#{arg1}#{arg2}。"
end

method_name("こんにちは", "どうぞよろしく")      # みなさんこんにちは。どうぞよろしく。
method_name("さようなら", "またあした")         # みなさんさようなら。またあした。

returnメソッドで返す戻り値

 戻り値とは、メソッドで行った処理の結果の値のこと。デフォルトではメソッドの最終的な結果が戻り値となるが、returnを使えば任意のタイミングで戻り値を得ることができる。しかし参考サイトによると、Rubyではメソッドはデフォルトで戻り値を返す仕様らしく、必要でなければreturnは省略すべきらしい。

参考文献
Rubyのメソッドの返り値にreturnを使うのは邪道なんですか #4

def method_name(arg1, arg2)
  return arg1 + arg2                   # 引数1と引数2を足す(文字列なら連結)した結果を返す
end

puts method_name(3, 5)                 # 引数として3と5を渡すと結果として8が出力される

 returnで真偽値を扱うことで条件分岐の処理が可能となる。真偽値を扱うメソッドを定義するときは、メソッド名の後に?をつけてmethod_name?と書く。

def method_name?(arg_name)        # メソッド名の後ろに?を付けると真偽判定の処理をしてくれる
  return arg_name < 1             # 引数が1未満かどうかの判定結果をtrue/falseで返す
end

puts arg_name?(2)                 # false (5は1未満ではない、つまり偽)

returnの行で一連の処理が一旦終了されるため、if文でreturnした場合には真が出るまで処理が見越される。ただしこのメソッドはもっとシンプルに書き換えることができる。ここではとりあえずreturnの特徴を理解するために例とした。

def method_name(arg_name)
  if arg_name >= 80               # 80以上でtrue
    return "80以上です"             # 判定結果がtrueであれば実行
  end
  return "80未満です"               # 判定結果がfalseであれば実行
end

puts method_name(70)              # 引数70を与える(という前提で上の処理を行う例)

 しかしこのif文はreturnを省略してもっとシンプルに改善できることは覚えておく。まだ触れないがunless文などを使えばもっと短くなるらしい。

def method_name(arg_name)
  if arg_name >= 80
    puts  "80以上です"
  else
    puts  "80未満です"
  end
end

method_name(70) 

キーワード引数

 キーワード引数とは、メソッド実行のときに渡す引数をハッシュの形式で指定する手法のこと。メソッド実行のときに引数のみを指定すると、その並び順通りに代入されるため、順番を間違えて代入しかねない。そこでキーワード引数を使って的確に任意のキー(arg1~3)に任意のバリュー(渡す引数)を指定できるようにできる。

def method_name(arg1, arg2, arg3)
  puts "#{arg1}の天気は#{arg2}です。#{arg3}。"
end

method_name(arg1:"今日", arg2:"晴れ", arg3:"洗濯日和です")     # 今日の天気は晴れです。洗濯日和です。
method_name(arg1:"明日", arg2:"雨", arg3:"傘を忘れずに")       # 今日の天気は雨です。傘を忘れずに。

クラス

用語

 オブジェクト指向では、何かの処理のまとまり、つまりオブジェクト(object)に様々な呼び名がある。

参考文献
初心者向けに徹底解説!オブジェクト指向とは?

  • クラス(class):設計図、テンプレート、コンセプト
  • プロパティ(property):データ、内容物
  • メソッド(method):処理
  • インスタンス(instance):生成物、実用物
  • インスタンス変数(instance variable):インスタンスのプロパティを格納する変数

クラスの定義とインスタンスの生成

 クラスとは、Webサイトやアプリなどで一定のパターンで情報を列挙するための元になるデザインの設計図などで使う。通販サイトの商品一覧や、SNSの一つ一つの投稿など。その商品サムネや投稿一つ一つがインスタンスにあたる。インスタンス変数を呼び出すにはインスタンス名.インスタンス変数名と書く。

class Class_name                                # クラス名は頭文字を大文字にするのが作法
  attr_accessor :insvar_name1                   # インスタンス変数
  attr_accessor :insvar_name2
end

instance_name1 = Menu.new                       # クラス名.newを代入すればインスタンスが複製できる
instance_name2 = Menu.new
instance_name3 = Menu.new

instance_name1.insvar_name1 = "文字列"
puts instance_name1.insvar_name1                # 文字列

インスタンスメソッド

 インスタンスメソッドとは、クラスの中で定義するメソッドのこと。クラスという設計図に、インスタンスを生成するための一連の処理(メソッド)を組み込んでおく感じ。インスタンスメソッドはインスタンス名.インスタンスメソッド名(引数)としないと呼び出せない。

class Class_name
  def method_name(arg_name)              # クラスの中でメソッドを定義
    return "与えた引数は#{arg_name}です"    # 与えた引数を文字列として返す処理
  end
end

instance_name1 = Class_name.new          # インスタンスを3つ生成
instance_name2 = Class_name.new
instance_name3 = Class_name.new

puts instance_name1.method_name("1")     # 与えた引数は1です
puts instance_name2.method_name("2")     # 与えた引数は2です
puts instance_name3.method_name("3")     # 与えた引数は3です

 attr_accessorで定義しておいたインスタンス変数をインスタンスメソッドの中で呼び出す書き方。インスタンス変数はある特定のインスタンスの中のインスタンスメソッドで使う場合のみselfとつける。インスタンス変数はインスタンスに所属する変数で、自分家の中だから細かい身分証明は必要ない的な。その代わりインスタンスの外からインスタンス変数としてやり取りが発生するときはinstance_name1.insvar_name1のように「インスタンス○○から来ました△△という変数です」と明記する感じ?そのときはプライベート空間ではなくて公共の場なのでselfモードではない。インスタンスの外ではインスタンス変数もインスタンスメソッドも名刺を差し出さなければならない。

class Class_name
  attr_accessor :insvar_name1
  attr_accessor :insvar_name2

  def method_name
    puts "インスタンス変数の値は#{self.insvar_name1}#{self.insvar_name2}です"
  end
end

instance_name = Class_name.new          # インスタンスを生成
instance_name.insvar_name1 = 1          # インスタンス変数1に1を代入
instance_name.insvar_name2 = 2          # インスタンス変数2に2を代入
instance_name.method_name               # インスタンス変数の値は1と2です  と出力される

initializeメソッド

 initializeとは初期化の意味。initializeメソッドとは、インスタンスメソッドのうちでもインスタンス生成と同時に実行されるメソッドのことで、インスタンスの初期状態を自動で設定するメソッドとも言える。よく分からないが、ウェブサービスにサインインする処理が新規インスタンス生成だとすると、入力されたユーザー情報がインスタンスメソッドに渡す引数に該当し、アカウント作成ボタンを押した瞬間に引数がインスタンス変数に代入されてアカウントが作成完了する、というのがinitializeメソッドの機能の活用例だと思う。つまり、initializeメソッドはクライアントサイドと対話するために役立つものと考えられる。

class Class_name
  attr_accessor :ins_var1              # インスタンス変数を定義
  attr_accessor :ins_var2

  def initialize(arg1:, arg2:)         # initializeメソッドを定義
    self.ins_var1 = arg1               # インスタンス変数をインスタンスメソッド引数と同義に扱う
    self.ins_var2 = arg2
  end

  def ins_method                       # インスタンスメソッドを定義
    return "インスタンス変数1に#{self.ins_var1}を、インスタンス変数2に#{self.ins_var2}を代入"
  end
end

instance_name1 = Class_name.new(arg1:"引数1", arg2:"引数2")     # インスタンス生成と同時に引数を渡す
puts instance_name1.ins_method       # インスタンス変数1に引数1を、インスタンス変数2に引数2を代入  と出力

 このコードをアカウント登録の例になぞらえて書き換えてみる。

class User                               # ユーザ登録をする処理のテンプレート
  attr_accessor :name                    # ユーザ登録情報1
  attr_accessor :email                   # ユーザ登録情報2

  def initialize(name:, email:)          # 登録フォームに入力された情報をユーザ登録情報に代入
    self.name = name
    self.email = email
  end

  def complete_message                   # 登録完了画面に表示するメッセージ
    return "ようこそ#{self.name}さん!! #{self.email}に送った承認メールをご確認ください。"
  end
end

user1 = User.new(name:"hoho", email:"xxxx@xxxx.xxx")          # この引数はユーザが入力する情報
puts user1.complete_message              # 登録完了メッセージを表示

# ようこそhohoさん!! xxxx@xxx@xxxに送った承認メールをご確認ください。

オブジェクト指向を擬人化してみる

 オブジェクト指向を擬人化してみる。まず「クラス」という一般家庭像がある。それに習って「インスタンス」という様々な家庭がある。家の中には「インスタンス変数」という住人がそれぞれ「プロパティ(値)」という個性を持っている。彼らは互いに関わり合って「インスタンスメソッド」というライフスタイルを営み、「引数」というライフスタイルに必要な道具や材料も随時用意し、そうして「戻り値」として各家庭で作ったものをおすそわけして近所付き合いが成り立っている。そのような多くの家庭が密集して「配列」というができる。住人たちは自分の家では簡易的なselfモードで、外では所属を明らかにする。他人に自分の家庭の日常について話すときは「うちの居間は〜」といい、自分の身内について話すときは「うちの姉が〜」というが、家族に対しては「居間に〜」や「姉貴は〜」で通じる。

継承

参考文献
【Ruby入門】includeの使い方とrequireとの違いについて解説!
クラス・モジュールの概念 Ruby
RubyのModuleの使い方とはいったい
継承, mix-in, include, extend について覚え書き
今更聞けない! Ruby の継承と mixin の概念を継承リストから学ぶ
mixinってなんなのさ
Rubyの「クラス継承」と「Mix-in」
【Ruby】Mix-inは継承なのか
継承のMix-inについて

require

 requireは外部ファイルやライブラリを読み込むメソッドのこと。他の言語(Cやjs)ではincludeが同じように機能している。継承というより連結。その外部ファイルでクラスやモジュールが定義されていたとしてもただそれが列挙されるだけで、別のクラスに中身が継承されるということはない。と解釈。

include

 Rubyのincludeは外部ファイルを読み込むことじゃなくて、moduleをclass内に読み込むメソッドらしい。moduleとは、メソッドや変数をパッケージングしたもの。classもメソッドや変数をパッケージングしているが、moduleとclassではいくつか機能が異なる。

  • Class_name.newでインスタンス生成できるが、module_name.newではエラーになる
  • 逆に言えばインスタンス生成できるmoduleがclassと同等と表現できる(?)

mixin

 moduleをclassに読み込むことをmixinという。

module

 moduleの使い道は主に2つ。

  • メソッドの格納庫
  • 名前空間を提供

 だとすると、機能的にはclassに似てはいるけど、その名の通り部品としての用途ということになる。例えば、classが車の設計図なら、moduleはボディとかホイールとかエンジンとかブレーキとかの各機能ごとの部品の集まりのこと。だからclassから様々な車種(インスタンス)が生産されるからといって、moduleから車を生産することはできない。

一言にまとめると

 classにmoduleをincludeする考え方をmixinという。というかmixinはincludeだけではない。

  • mixin
    • include
    • extend
    • prepend

クラスの継承

 同じ機能を持ったクラスを複製するときに、わざわざ1から作り直す手間を省くために元となるクラスから内容を引き継ぐことを継承という。クラスの継承はdef 子クラス名 < 親クラス名で定義できる。継承すると、親クラスと同じインスタンス変数とインスタンスメソッドが使えるクラスとなる。また、親クラスのことを「スーパークラス」という。

file1.rb
class Class1                # クラス1を定義
  attr_accessor :insver1    # インスタンス変数1を定義
  attr_accessor :insver2    # インスタンス変数2を定義

  def insmethod             # インスタンスメソッドを定義
    return "insvar1は#{self.insvar1}、insvar2は#{self.insvar2}"
  end
end
file2.rb
require "./file1"           # ファイル1と連結

class Class2 < Class1  # 親のクラス1の中身を、子のクラス2に継承
  attr_accessor :insvar3    # インスタンス変数3を追加

  def insmethod1             # インスタンスメソッドを上書き
    return "insvar1は#{self.insvar1}、insvar2は#{self.insvar2}、insvar3は#{self.insvar3}"
  end

  def insmethod2
    return "#{insvar3}は子クラスで扱うインスタンス変数"
  end
end
file3.rb
require "./file2"            # ファイル2と連結

instance1 = Class2.new(insver1:"引数1", insvar2:"引数2")  # 子クラスで生成したインスタンスで、親クラスのインスタンス変数に引数を渡す
puts instance1.insvar1          # 引数1  と出力される
puts instance1.insmethod1        # insvar1は引数1、insvar2は引数2  と出力される

オーバーライド

 インスタンスメソッド、initializeメソッド

superとは

参考文献
【Java入門】superとは?superの意味と使い方を解説!
スーパークラスのメソッドを呼び出す
引数があるスーパークラスのメソッドを呼び出す

 superとは、サブクラスでオーバーライドされた変数やインスタンスを参照する処理を意味する。子クラスのインスタンスから、親クラスのインスタンスのメンバにアクセスして値を参照するために使う。superでアクセスできるのは1世代上の親クラスまでで、2回以上継承を繰り返すときには使えない。つまり、ターミナルでいうsudoみたいに少し強い権力を行使するときに使う呪文的なもの。

 以下の例では、子クラス内のインスタンスメソッド内でsuperメソッドを実行すると、スーパークラス内の同じメソッド名を参照して実行するもの。

class Class1                       # スーパークラスとなるClass1を定義
  def insmethod(insvar1)           # インスタンスメソッドを定義
    print "#{insvar1} 文字列 "      # 引数を出力
  end
end

class Class2 < Class1              # Class1をClass2に継承
  def insmethod(insvar1)           # インスタンスメソッドを上書き定義
    super                          # スーパークラスの中身を参照
    print "追加文字列"              # インスタンスメソッドに新たな処理を追加
  end
end

class2 = Class2.new               # 子クラスでインスタンスを生成
class2.insmethod("引数")           # 「引数 文字列 追加文字列」と出力される

initializeメソッドの場合、superに引数を渡す必要がある。(ここらへん違うっぽい)

class Class1                       # スーパークラスとなるClass1を定義
  def insmethod(insvar1)           # インスタンスメソッドを定義
    print "#{insvar1} 文字列 "      # 引数を出力
  end
end

class Class2 < Class1              # Class1をClass2に継承
  def insmethod(insvar1)           # インスタンスメソッドを上書き定義
    super(icsvar1)                          # スーパークラスの中身を参照
    print "追加文字列"              # インスタンスメソッドに新たな処理を追加
  end
end

class2 = Class2.new               # 子クラスでインスタンスを生成
class2.insmethod("引数")           # 「引数 文字列 追加文字列」と出力される

Dateクラス

クラスメソッド

入力

gets.chomp

初心者のコードの認識の仕方

複雑なコードを見ても、それがどういう処理なのか読み取るのに時間がかかるしとてもしんどい。勉強する上で大きな弊害となる。なのでコードをパッと見て理解するコツのようなものを試行錯誤してみた。

  • オブジェクト指向らしく塊で捉える
  • 入れ子状の箱のビジュアルに置き換える
  • オブジェクトの種類を覚えておく

 といったことを瞬時に行えるようにしたい。

  • require
  • include
  • クラス
  • モジュール
  • メソッド(関数)
  • instance
  • instance変数
  • 変数
  • プロパティ(値)
  • 引数
  • 戻り値
  • 配列・ハッシュ
  • 条件分岐
  • 繰り返し

 などのイメージを型として脳内に持っておきたい。