[Ruby]他言語使用者のためのRuby入門知識まとめ

  • 208
    Like
  • 9
    Comment
More than 1 year has passed since last update.

私は普段主にC#やjavascriptを使ったりしているのですが、巷で話題のRubyにもそろそろ手を出そうと基礎を勉強し始めました。勉強の過程でRubyって今まで触れてきた言語と思ったより違うなぁと感じました。こういう違いを俯瞰できたら勉強しやすいと思ったのでこの記事を書いた次第です。
これをおさえておけば、ある程度ソースが読めるかも、という見慣れない構文や決まりなどをまとめています。あれもこれもと書いていると長くなってしまいました...。概要をまとめただけで、詳細は他のページや記事に任せるようにしています。
「これは他の言語にも共通じゃないか」と思われることもあるかもしれませんが、ご容赦ください。
※バージョンはRuby2.0を想定

変数

変数の種類は、変数名の前にpublicとかprivateをつけるのではなく、変数名に特徴を持たせることで識別する。

ローカル変数

スコープが変数がかかれたブロックに限定される変数。
変数名は 小文字のアルファベットまたは_で始める。
ローカル変数xに100を代入する場合はx = 100と書けばおk。

グローバル変数

スコープがプログラム全体の変数。
変数名の前に$をつける。
グローバル変数$xに100を代入する場合は$x = 100と書く。

クラス変数

クラス変数はクラス定義の中で定義され、クラスの特異メソッドやインスタンスメソッドから参照・代入できる。
クラス、サブクラスのみから参照可能。
クラス変数の値はクラスのすべてのインスタンスで共有する。
変数名の前に@@をつける。

インスタンス変数

インスタンス内に保持する変数。
変数名の前に@をつける。
インスタンス変数@xに100を代入する場合は@x = 100と書く。

定数

代入は原則1度。2回目以降は警告が出る。
先頭がアルファベット大文字。
慣例としてコードを見た感じ定数名全体が大文字であることが多いように思う。
定数CONSTに4を代入する場合CONST = 4と書く。

オブジェクト

ハッシュ

他の言語にもある、ハッシュテーブルや連想配列のこと。
キーとオブジェクトをセットで格納する。
キーとオブジェクトは任意の種類のオブジェクトである。
キーにはしばしばシンボル(後述)が使用される。

hashの書き方
#この例では"Taro"がキー、"Tarochan"が値
hash = {:name => "Taro", :nickname => "Tarochan"}

シンボル

ソース上では文字列のように見え、内部では整数として扱われる。文字列の格好をした整数とも言える。直接文字列を処理するよりも早く処理ができるので、文字列自体が必要でない場合に利用される。具体的には以下の通り。

用途

  • ハッシュのキー
  • アクセサの引数で渡すインスタンス名
  • メソッド引数で渡すメソッド名
  • C言語におけるのenumの代用

メリット

  • 新しく文字列を生成しない分やや効率がよく、比較も高速。
  • 文字の意味がはっきりするのでコードが読みやすくなる
  • immutable なので内容を書き換えられる心配がない
symbolの書き方
symbol = :hoge

正規表現

Rubyが得意とする文字列処理を実現する1つの機能。
文字列からパターンを切り出したりすることができる。
以下は文字列からパターンに一致する位置を返す文。

正規表現の書き方
#/パターン/ =~ マッチングする文字列
/Taro/ =~ "Yamada Taro deus" #=>7

#デフォルトでは大文字と小文字は区別される。
#nilは他言語のnullと同じような意味
/taro/ =~ "Yamada Taro desu" #=>nil

#パターンの後ろにiを追加すると大文字小文字の区別がなくなる。
/taro/i =~ "Yamada Taro desu" #=>7

メソッド

デフォルトの引数

デフォルト値が設定できる。

引数のデフォルト値の定義
def numFunc(a, b="test")
  puts a + " and " + b
end

numFunc("check")  #=> check and test
numFunc("check", "check")    #=> check and check

引数の数を定めないメソッド

引数の数を自由に変えることが可能なメソッドを作ることができる。
形式は*変数名

引数の数が自由なメソッド
def hoge(*args)
  args
end

puts hoge(1, 2, 3)
#=> 1
#  2
#  3

ブロック付きメソッド

このへんについては詳しい記事がありました。
[Ruby] ブロックとProcをちゃんと理解する

yield

ブロック付きメソッドというメソッドからブロックを呼び出すときに使用する。

yieldの使い方
#メソッドの定義
def testFunc
  yield(1,3)
end

#呼び出されるブロック
testFunc do |a,b|
  puts a + b
end
# => 4

Proc

ブロックをオブジェクト化したもの。
メソッドで受け渡しすることができるようになる。

procの使用例
#proc定義
test = Proc.new{ puts  "a" }

#実行
test.call
# => a

メソッドの定義とか

alias

メソッドに別名をつけることができる。
別名が付けられたメソッドは、その時点でのメソッドの定義を引き継ぐ。元のメソッドの定義が変更されても、再定義前の定義と同じ動作をする。利用シーンとしてはあるメソッドの動作を変更するとき、変更前のメソッドの動作を仕様したいときが挙げられる。
書き方は
alias 別名 元の名前
またはシンボルを利用して書けば
alias :別名 :元の名前
と書ける。

alias使用例
#メソッドの定義
def test
  "test"
end

# エイリアス(別名)を設定
# 元の定義を別の場所に保持する。
alias :_orig_test :test

#testの再定義
def test
  #元のメソッドの定義を仕様
  "#{_orig_test} dayo"
end

puts test
# => "test dayo"

undef

メソッドの定義を取り消すキーワード。
具体的にはメソッド名とメソッドの定義との関係を切離す。
undef メソッド名で使える。

クラス

初期化メソッド:initialize

newメソッドによりインスタンスが生成された時に実行するメソッド。

initializeメソッド
class Test
  #初期化
  def initialize(initvalue = "初期値")
    #インスタンス変数を初期化
    @init = initvalue
  end
  def whtinit
    puts @init
  end
end

a = Test.new
a.whtinit  # => 初期値
b = Test.new("test")
b.whtinit  # => test

アクセスメソッド

Rubyではインスタンスの外部からインスタンス変数を直接参照・代入することができないので、メソッドを介して参照・代入する。

アクセスメソッドの定義1
class TestClass
  #@fooを呼び出すためのメソッド
  def foo
    @foo
  end

  #@fooに代入するためのメソッド
  def foo=(value)
    @foo = value
  end
end

記述を簡略化する方法がある。
上記のように参照・代入の両方を許容する場合以下のようにも書ける。

アクセスメソッドの定義2
class TestClass
  #@fooをfooで参照・代入
  attr_accessor :foo
end

参照のみ許可したい場合はattr_reader
代入のみ許可したい場合はattr_writerに変更する。

変数self

インスタンスメソッドで、メソッドのレシーバ自身を参照する変数。

クラスの定数

クラス内に定数を定義することもできる。
外部から参照するには、クラス名::定数名で呼び出す。

定数の定義と外部からの呼び出し
class TestClass
  Const = "testtest"
end

puts TestClass::Const

クラスメソッド

クラスオブジェクトをレシーバとするメソッドである。
形式は特異クラス形式という class << クラス名 ~ end
または特異メソッド形式という def クラス名.メソッド名 ~ end

特異クラス

先ほど挙げたclass << クラス名 ~ endは特異クラスという。
特異クラスを利用してインスタンスメソッドを記述することが可能。
以下の解説ページが詳しい。
Ruby 初級者のための class << self の話 (または特異クラスとメタクラス)

継承

既存のクラスの性質を引き継いだクラスを新しく作るのが継承。
他のオブジェクト指向言語と考え方は似ている。
ただしRubyにおいて継承できるクラスは1つまでである。

クラスの継承
#クラスSuperClassを継承してNewClassクラスを定義
class NewClass < SuperClass
  #クラス定義の内容
  #...
end

条件文

unless

「〜でなければ」という条件文。
if(!条件)と同じ。

unlessの文
unless a > b
  puts "!(a > b)"
end

モジュール

モジュールとは

Ruby特有の機能の1つで、クラスとよく似た概念である。
クラスと比較して以下のような性質がある。

  • インスタンスを生成出来ない。
  • モジュールクラスを継承できない。

モジュールは単純継承のモデルを保ったまま、複数のクラスの機能を共有するための機能。
モジュールの作り方は下記のようになる。
モジュール名は大文字で始める。

モジュールの作成
module TestModule
  #モジュールの定義
  #...
end

モジュールに関する詳しい説明がこちら
Rubyのオブジェクト指向におけるクラスとモジュール、継承、Mixin、アクセス制御の使い方 (4/5)

どうしてモジュールという概念を作ったかという背景が書いてある記事が以下。
まつもと直伝 プログラミングのオキテ 第3回(3)多重継承の光と影

モジュールには下記の大きく分けて2種類の役割がある。

名前空間を提供

名前空間とは、定数・メソッド・クラスなどを管理する単位である。
モジュールの提供する名前空間でオブジェクトの名前の定義をわけることで、衝突を防ぐことができる。

Mix-inで機能を提供

モジュールをクラスやモジュールに取り入れることをMix-inという。
定義にincludeを使ってモジュールを取り込めばメソッドや定数を他のクラスやモジュールでも使用することができる。

モジュールのメソッドの使用

メソッドを定義するのみでは使用できず、メソッドをモジュール関数として外部に公開する必要がある。シンボルを使って以下のように記述する。

モジュール内のメソッドの使用
module TestModule
  def test
    puts self
  end
  module_function :test
end

TestModule.test #=> TestModule

モジュールによる特異メソッドの実装

モジュールを特異クラスにインクルードすることでオブジェクトにモジュールの機能を追加できる。
具体的にはオブジェクトからextendメソッドを用いる。

モジュールで特異メソッドの実装
module TestModule
  def hello(n)
    n.times do
      print "hello"
    end
  end
end

str = "test"
str.extend(TestModule)  #=>オブジェクトにMix-in

str.hello(5)
#=> hellohellohellohellohello

繰り返し

times

一定回数繰り返しの構文。
カウンタ変数の値を使わない場合はこれでおk。

timesによる繰り返し
5.times do
  print "a"
end
# => aaaaa

for

よくあるfor文と同じ。
配列の要素に対して、繰り返しを行なうこともできる。

for文
#一般的なfor文
for i in 1..3
  print i
end
# => 123

#配列に対して使用
for str in ["R", "u", "b", "y"]
  print str
end
# => Ruby

while

条件を満たしていれば繰り返す。

whileによる繰り返し
i = 0
while i < 5
  print "a"
  i += 1
end
# => aaaaa

until

unlessの繰り返し版みたいな感じ。whileの逆版。
条件を満たさなければ繰り返す。

untilによる繰り返し
i = 5
until i == 0
  print "a"
  i += 1
end
# => aaaaa

each

コレクションのようなオブジェクトの集合体に対して繰り返し処理が可能。
C#におけるforeachのような処理ができる。

eachによる繰り返し
lang = ["Ruby", "Python", "Perl"]
lang.each do |name|
  print name + " "
end
# => Ruby Python Perl

loop

いわゆる無限ループ。

loopによる繰り返し
loop do
  print "a"
end
# => aaaaaa...

繰り返し制御

break

break実行時、繰り返しのブロックを抜ける。
多重ループでは内側のループを一つ抜ける。
(他の言語だとすべて抜けたりする。)

next

他の言語のcontinueに相当。
next実行時、次の回のループの始めに飛ぶ。
つまり次のアイテムに対してループを始める。

redo

redo実行時、その回のループの始めに飛ぶ。
つまり同じアイテムに対して、もう一度ループを始める。

演算子

真偽値の扱いについて

論理演算子について説明する前に、Rubyにおける真偽値の扱いをおさえておく。Rubyにおいて偽と扱われるのはfalseまたはnilのみであり、それ以外はすべて真として扱われる。特に他の言語では偽と評価されうる0""[]のいずれもRubyでは真と評価されることには注意が必要である。

論理演算子によるエラー回避

配列の要素が存在しない状態でその要素を参照しようとするとエラーが起こる。
それを避けるために条件分岐を使用して参照を行なう。

条件文を用いたエラー回避
element = nil
if ary
  element = ary[0]
end

これを論理演算子で表現すると短く書ける。
論理式の評価がtruefalseが確定した時点で処理が終了することを活かしている。

論理演算子を応用したエラー回避
element = ary && ary[0]

これはaryfalseまたはnilでないときのみ、右辺の値がary[0]と更新されることを利用した例である。

以下のようにifの後置により省略することも可能

ifの後置による略記
element = nil
element = ary[0] if ary

さらに、三項演算子を用いても同様の働きをする処理が書ける。

三項演算子による略記
element = ary ? ary[0] : nil

デフォルト値の代入

||の代入演算子を用いることで、ある変数の値がfalseまたはnilのときのみ値を代入することができる。

var ||= 100
#以下に相当(詳細は下記URL参照)
var || var = 100

書き換えた代入文については以下の記事が詳しいです。
『a ||= b』は『a = a || b』ではなく『a || a = b』とほぼ同じ意味、と覚えましょう - わからん

範囲演算子

Rubyには値の範囲を示すRangeオブジェクトが存在する。
他のオブジェクトの生成法と同じようにRange.new(from, to)と書けるが、from..toと省略して書くことも可能である。
これを実現するメソッドとしてsuccメソッドというものがある。これは変数の数字あるいは文字の次の値を返す。

succの例
str = "c" #=>"c"
str = val.succ #=> "d"
var = 50 #=> 50
var = var.succ #=> 51

演算子の定義

Rubyの演算子はインスタンスメソッドとして実装されているため、ユーザによる定義が可能である。

二項演算子

+-*をはじめとしたその他二項演算子をユーザは定義可能

単項演算子

+-~!の4つが定義可能である。
+@-@~@!@というインスタンスメソッドとして定義することで使用することができる。

以下は+の二項演算子と-の単項演算子を、二次元座標を保持するPointクラスに定義した例である。

演算子の定義例
class Point
  attr_accessor :x, :y

  def initialize(x, y)
    @x, @y = x, y
  end

  def show
    "(#{x}, #{y})"
  end

  #x,yそれぞれの和を計算する二項演算子を定義
  def +(other) 
    self.class.new(x + other.x, y + other.y)
  end

  #x,yの値に-1を乗じたものを返す単項演算子を定義
  def -@
    self.class.new(-x, -y)
  end
end

p0 = Point.new(3.0, 4.0)
p1 = Point.new(1.0, 2.0)
puts (p0 + p1).show #=> (4.0, 6.0)
puts (-p0).show #=> (-3.0, -4.0)

お決まり的なこと

文末に;が不要

ここまでしれっと書いてきましたが、割とびっくりしたところ。

ライブラリのロード

ライブラリや、別に作った他の***.rbを読み込んで使用するためにはCでおなじみのincludeのようなことを記述する必要がある。Rubyではrequireキーワードを使用する。

同じディレクトリのhoge.rbの取り込み
require "./hoge"

変数への多重代入

Rubyでは1式で複数の変数に代入が可能。

一括代入
#多重代入
hoge, foo, bar = 3, 4, 5

#これと同じ
hoge = 3
foo = 4
bar = 5

ブールを返すメソッドの名前

truefalseを返すメソッド名の末尾には?をつけるしきたりがある。
わかりやすくするためで、無くても実行は可能。
定義するときこれに従うと親切。
例:String型のString#empty?メソッド

単項インクリメント、デクリメントはない

Rubyの言語の仕様の都合で定義できていないらしいです。
よくわかっていないですが以下にまつもとゆきひろ氏本人のコメントがあります。
http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-list/5323

@scivola さんのコメントに詳しい記述があります。

ブロックの書き方

do-endでくくる

doは省略されることもある。
doが省略出来るのはブロックでない制御構造whileuntilfordoである。ブロックのdo~endにおけるdoは省略出来ない。

do,endを使ったブロック記法
(1..5).each do |i|
  print i
end
# => 12345

{}でくくる

おそらく多くの言語で使われる形。
1行で記すときに構造が認識しやすい。

{}を使ったブロック記法
(1..5).each {|i| print i}

並べ替えにおけるsortとsort_byの違い

ブロックの評価方法によって速度が違う。
以下のとおり。
instance method Enumerable#sort_by
sort + ブロックではなくsort_by + ブロックを使おう

もっと色々な書き方とか

覚えておくとよさそうな書き方をまとめた記事。
読んでいて楽しい。
Railsを触る際知っていると便利なRubyの基礎 [ブロックとかシンボルとか]

最後に

良いコードを読むのが一番

Trending Ruby repositories on GitHub today
ここにはその日に更新された中で最もスターがついた(多くの人がイイねした)Rubyのリポジトリが表示される。
今回いくつか構文や記法を紹介したが、実用的でないものがあるかもしれない。
やはりまず覚えるべきは良く使われる表現だと思うので、コードを読んでわからないところは調べるというアプローチが一番上達する気がする。

参考

オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル
http://docs.ruby-lang.org/ja/2.0.0/doc/index.html