Rubyでびっくりしないために
別な言語を覚える時のハードルの1つに、その言語の記法だったり慣用だったりがある。
Rubyのそういった特徴の私的メモ。
基本
言語の特徴
- スクリプト言語でオブジェクト指向言語。
- ほぼ全てがオブジェクトで構成されている。
- イテレーション処理とクロージャ処理に長けていて、データの集合をDBと組み合わせて扱うのが得意。
- 現代のWeb系サービス向け(Railsの影響が大きいかな)。
- 書き方は結構ゆるめで、敷居が低い。
- ブロックでカッコを使わないなど、多少癖はある。
- 全てがオブジェクトなので型はないと思って良い。動的型付け言語。
- Ruby といえば、 Ruby on Rails というくらい、Railsとは切っても切り離せない関係。
- なので、初心者がRailsから入ると、あるクラスやメソッドが、Rails由来なのか言語標準なのかを判断するのに苦労はする。(気にしないでググる、って手もある)
-
irb
という、強力なコマンドラインインタプリタがある。 - モジュールは豊富。導入も簡単。(Bundlerという仕組みが用意されている)
- ドキュメントはネット上にも豊富。
- だけどなぜか探しにくい。いろいろな人が色々な所に書いていて、どれが最新で正しいか判断しにくい。GoogleやQiitaで上位にランクしている記事が数年前のバージョンを対象とした古い記事だったりとか。Rubyに限らないけど、人口に膾炙されているだけに、そういう傾向が目立つ気がする。
- 「設定より規約」の影響なのか、「当然知っているだろうから(知っている人が対象だから)説明しないよ。あえてね」っぽいのが多い印象。
- なので、場合によってはソースを見た方が早い、ってこともある。
文法
C由来の言語のいいところを取りつつ、nil
、;
を使わない、制御構造も{}
の代わりにdo ~ end
など、独自の設計思想がある。逆に言えば、;
や{}
で区切ったり囲ったりする文法に馴染んだ人には、慣れるまで少し時間が必要かも。
- JavaやJavascriptのNULLにあたるものは、
nil
(ニル)。LISPからの影響かしらん。 -
nil
とfalse
以外は全てtrue
。0
もtrue
。""
もtrue
。- なお、
undefined
値はない。
- なお、
- 変数宣言は要らない。
- 全ての変数はオブジェクトへの参照。そのため関数への値渡しもない。
- 行末のセミコロンは要らない。明示的に
;
を入れて式を区切ることは可能。- セミコロンが要らないために、逆に
¥
で明示的に行を継続させる方法も用意されている。例えば、メソッドの引数を改行後に与えてしまうとRubyは引数が無いと判断してしまう。こういう場合、()
で囲むか、¥
を使う。
- セミコロンが要らないために、逆に
somearray.push # <- ()で囲むか、`¥`で行が継続していることの明示が必要
new_array_member
-
:任意の文字列
でシンボルを表す。ラベルのようなもの。内部的に番号管理されるので、速度面やメモリ面での利点がある。なによりコードの可読性や文脈が向上するのがありがたい。 - コメントはPerlと同様
#
。#
以降がコメントとみなされる。C/C++、Javascriptのような複数行コメント/* */
はない。- ブロックコメントがあり、
=begin
~=end
。"begin"は省略できない。
- ブロックコメントがあり、
-
#coding: utf-8
として(マジックコメント)、エンコードを指定して処理系に伝える。ソースコードの1行目ないしは2行目(シェバンがある場合)に書く。
式
- 全てが式。
if
なども式。式なので結果が発生する。-
if
のような他言語の制御文にあたるものを式として意識する必要は、普段は持たなくて良い。- メソッドの返り値だけ気をつける。
- メソッドの返り値は
return
で明示的に返すこともできるが、Perlのように、最後の演算結果が暗黙的に返り値となる。
-
- 代入式で一度に複数個への代入が可能。(
a,b = 1,2
) - 三項演算子(
A ? X : Y
)は、それぞれの記号の前後は必ずスペースが必要。演算の優先順位的に誤った解釈が発生しうるから。- 言語仕様としてメソッド名に
?
を与えられるので、A?
はメソッド名に、またX:
はハッシュ中のシンボルを使ったキーにRubyが解釈してしまう。
- 言語仕様としてメソッド名に
- 多方向分岐では、
switch
ではなく、Bashとおなじcase
を使う。-
if
もcase
も、ブロックを閉じるのは、fi
やesac
ではなく、全てend
。 - なので、
end
ばかり並んで可読性が悪くなるようなネストにならないよう、注意。
-
- 繰り返し制御文として、
for
,while
,until
がある。-
for
はまず利用しないだろう。Enumerable
なオブジェクトであれば、メソッドで繰り返し処理(each
など)が豊富に用意されているから。
-
-
"Format String" % values"
で、sprintf("Format String",values)
のシンタックスシュガーになる。- なので、Rubyistは
sprintf
は使わないそうな。
- なので、Rubyistは
- 変数展開可能な文字列中で、
#{}
でくくって、式を囲める。
変数・定数
- 宣言されたクラスやモジュールなどは、すべて定数として管理されている。
- 定数は大文字で始まる。全て大文字である必要は、ない。
- グローバル変数は頭に
$
をつける。-
$_
のように、Perl と同じような組み込みの特殊変数もある。
-
- インスタンス変数には頭に
@
をつける。クラス変数には頭に@@
をつける。 -
::
は、定数参照時の演算子。Hoge::Fuga::Piyopiyo
のように使う。なお、::Hogehoge
でトップレベルの定数を指す。
計算、演算
-
++
、--
演算子はない。 - 演算子は、その実、メソッドである。(
=
以外の演算子はメソッドのようだ) - 何も考えずに
1/2
、10/3
と演算しても、結果はそれぞれ0
、3
になる。Integerオブジェクトの/
メソッドがそうしているから。結果をFloatにする場合は、除数か被除数をFloatにする。 -
..
と...
で範囲演算子。フリップフロップも使える。 - Perl 同様、
and
,or
,not
といった、優先度の低い論理演算子が用意されている。
配列、ハッシュ
# 配列の初期化
a0 = [] # 空の配列
a1 = ["zero","uno","dos","tres"]
# ハッシュの初期化
h0 = {} # 空のハッシュ
h1 = {"alpha"=>1, "bravo"=>2 } # key が文字列
h2 = { :first => "alpha", :second => "bravo" } # key にシンボルを使用する方法1
h3 = { third: "nagashima", forth: "delta" } # key にシンボルを使用する方法2
配列
- 配列の参照は、
[]
を使う。-
[]
も、メソッドである。
-
-
*
は、配列の内容を展開する。
irb(main):001:0> d=[1,2,3,4]
=> [1, 2, 3, 4]
irb(main):002:0> p d
[1, 2, 3, 4]
=> [1, 2, 3, 4]
irb(main):003:0> p *d
1
2
3
4
=> [1, 2, 3, 4]
- また、
*
をメソッド側の仮引数につけると、この場合は配列化をする(可変長引数などを使う場合に便利)。
irb(main):001:0> def fuga(a, *b)
irb(main):002:1> p a; p b
irb(main):003:1> end
=> nil
irb(main):004:0> fuga(1,2,3,4)
1
[2, 3, 4]
=> [2, 3, 4]
irb(main):005:0>
ハッシュ
- ハッシュは、上記のように初期化の際には
{}
を使ったりするが、参照時には配列と同じ[]
を使う。- 配列同様
[]
はメソッド。
- 配列同様
- また、ハッシュは、デフォルト値を設定できる。
クラス/モジュール/メソッド/ブロック
クラス
- クラスの継承は単一継承で、複数クラスからの多重継承はできない(モジュールで対応)。
-
クラスやモジュールはネストできる。
- ネストは、名前空間を共有する以外の意味はなく、継承などが生まれるわけではない。
- クラスとモジュールで、名前空間を共有できる。
- クラス名は定数として管理されるため、大文字で始まらないと処理系に怒られる。また、キャメライズせよ。
- クラスにエイリアス(alias)が設定できる。
- クラス定義直下のスコープで実行文を置ける。またそれらは逐次的に実行される。
- クラス定数などを置いたり、
attr_accessor
(アクセサを定義する)など、ユーティリティなメソッド呼び出し文を置いたりする。
- クラス定数などを置いたり、
-
Klass#initialize
メソッドが初期化関数で、Klass.new
メソッドがコールされるとこの関数が呼び出される。-
Klass
に関しては後述「その他」を参照。
-
- メソッド内で
super
を呼び出して、スーパークラス(親クラス)のメソッドを呼び出せる。親クラスそのものではなくてメソッドのコードなので注意。 -
private
より後ろのメソッド定義は、Privateメソッドになる。
モジュール
- モジュールは、インスタンス化できない、機能(部品)を提供としたクラス、といったところ。
- モジュール名も大文字で始まらないと怒られる。同様にキャメライズせよ。
- クラスは、複数のモジュールを取り込むことができる。(Mix-in)
- ざっと言えば、class内で
include
でインスタンスメソッドとして、extend
でクラスメソッドとして、任意のモジュールのメソッドを取り込める(参考)。 - モジュール内でクラスを定義している例があるが、モジュールで名前空間作っているということ。
module Hoge::Fuga
class Piyopiyo # Hoge::Fuga::Piyopiyo
メソッド
- メソッド呼び出し時に、引数を
()
で括らなくてもよい。- ただし、引数の渡し方によっては、
()
で括らないとうまく解釈されない場合があるので、場合に応じてくくる。
- ただし、引数の渡し方によっては、
- メソッドの最後の演算結果(評価)がメソッドの返り値となる。
-
return
で明示的に返すことも可能。
-
- メソッド名の末尾に
!
をつけたものは、破壊的なもの、取り扱いに注意が必要なメソッド、という慣用がある。- 配列の中身そのものを置き換える、とか、エラーを発生させるとか。
- メソッド名の末尾に
?
をつけたものは、Boolean値を返すもの、という慣用がある。 - 引数にデフォルト値を指定できる。
-
ブロックを渡す場合は、呼び出し部分の直後に
{}
かdo ~ end
で定義することで渡すことが可能。- このブロック部分は、メソッド側から、
yield
によって呼ぶことができる。
- このブロック部分は、メソッド側から、
def product(a,b)
yield a*b
end
product(3,4) do |r|
p "Result %d" % r
end
- ブロック内ローカル変数のシャドウイング(ブロック外側の同名の変数はブロック側から見えないこと)はない。
- ブロックのブロックパラメータ宣言部で、
;
のあとに続けて、シャドウイング対象の変数を指定できる。
- ブロックのブロックパラメータ宣言部で、
-
{}
もdo ~ end
も、どちらもブロックコードとなるが、構文解釈の優先順位が{}と比べて変わるので注意。do ~ end
は結合度が弱く、処理系の解釈が意図しない結果となる場合がある。
正規表現
- m修飾子が、Perl と違う。
その他
- PerlのPod同様、Rdocがある。
- 幾つかコーディング規約が散見できる(ex. Cookpad, GitHub 有志翻訳版、とか)が、ざっくり言えば タブは半角空白2文字 がマスト。
- あとは、命名規則なども参考に。
- ドキュメントやコードのの慣用表現がいくつかある。
- 式の結果を表すコメント部分は、
# => xxx
のように表す。 - サンプルコードや実際のコードで、クラスそのものを表すのに、
class
だと読み手が混乱してしまったり、そもそも予約語で使えなかったりするので、Klass
,klass
という表記が使われる。(他には、aliaz
とか) - あるメソッドが呼ばれる時、そのメソッドのオブジェクトそのものをさして レシーバー(reciever)と称す。
- クラスメソッドは Klass.method で表す。一方、インスタンスメソッドは Klass#method で表す。コード上はどちらも
reciever.method
で呼び出す。
- 式の結果を表すコメント部分は、