こんにちは、hachi8833です。「Rubyスタイルガイドを読む」、今回は文法編です。
文法の項は70ほどもあるので、何回かに分割しようと思います。
文法
::
は、定数(クラスやモジュールも含む)、コンストラクタ(Array()
やNokogiri::HTML()
など)の参照にのみ使う
Use
::
only to reference constants(this includes classes and modules) and constructors (likeArray()
orNokogiri::HTML()
).
Do not use::
for regular method invocation.
通常のメソッド呼び出しでも::
を使えてしまいますが、それだと読みにくくなることが容易に想像できるので利用を制限しているのだと思います。
# 不可
SomeClass::some_method
some_object::some_method
# 良好
SomeClass.some_method
some_object.some_method
SomeModule::SomeClass::SOME_CONST
SomeModule::SomeClass()
メソッド定義にパラメータがない場合は、def
行のメソッドにかっこ()
をつけない
Use
def
with parentheses when there are parameters. Omit the parentheses when the method doesn’t accept any parameters.
逆にメソッド定義にパラメータがある場合は明示的に()
をつけます。
# 不可
def some_method()
# body omitted
end
# 良好
def some_method
# body omitted
end
# 不可
def some_method_with_parameters param1, param2
# body omitted
end
# 良好
def some_method_with_parameters(param1, param2)
# body omitted
end
Rubyではメソッド呼び出しなどでパラメータのかっこを省略できますし、呼び出しではかっこを省略するスタイルが広く使われていますが、本スタイルではメソッドの定義・呼び出しともにかっこを省略しないスタイルになっています(メソッド呼び出しについては後述)。
メソッド呼び出しのかっこ()
は省略しない、特に第1引数がかっこ()
を使った式の場合
Use parentheses around the arguments of method invocations, especially if the first argument begins with an open parenthesis
(
, as inf((3 + 2) + 1)
.
# 不可
x = Math.sin y
# 良好
x = Math.sin(y)
# 不可
array.delete e
# 良好
array.delete(e)
# 不可
temperance = Person.new 'Temperance', 30
# 良好
temperance = Person.new('Temperance', 30)
「特に第1引数が…」のサンプルを以下に取り出してみました。
f (3 + 2) + 1 # 読みにくい
f((3 + 2) + 1) # 読みやすい
次の場合にのみかっこ()
を省略する
- 引数のないメソッド呼び出し
# 不可
Kernel.exit!()
2.even?()
fork()
'test'.upcase()
# 良好
Kernel.exit!
2.even?
fork
'test'.upcase
- 内部DSLの一部となっているメソッド呼び出し
DSL(ドメイン固有言語: Domain Specific Language)にはRake、Rails、RSpecなどが該当します。RubyではこうしたDSL処理系を比較的簡単に作ることができます。
# 不可
expect(bowling.score).to eq 0
# 良好
expect(bowling.score).to eq(0)
- ステータスをキーワードで指定するメソッド
確かに、:name
などのシンボルならかっこがなくても十分視認性が確保されそうですし、むしろかっこがあるとうるさく見えそうです。
class Person
# 不可
attr_reader(:name, :age)
# 良好
attr_reader :name, :age
# 本文は省略
end
# 不可
puts(temperance.age)
# 良好
puts temperance.age
他の言語の経験者は最初なかなか慣れないかもしれませんが、慣れるとRubyのメソッド呼び出しにおけるかっこ省略スタイルはむしろ読みやすく感じられます(注: 個人の感想です)。
とはいうものの、込み入ったメソッド呼び出しでかっこをつけないと読みづらくなったり誤動作したりすることがあるので、そういった場合にはかっこをつけることになります。
このあたりは既存のコードや会社のコーディングスタイルに合わせて変えることもあるかと思います。
オプション引数は引数リストの末尾に置く
Define optional arguments at the end of the list of arguments. Ruby has some unexpected results when calling methods that have optional arguments at the front of the list.
オプション引数(optional arguments)とは、a = 1
のようにデフォルト値を指定した引数のことであり、その引数の値を省略したときにデフォルト値が使われます。
# 不可
def some_method(a = 1, b = 2, c, d)
puts "#{a}, #{b}, #{c}, #{d}"
end
some_method('w', 'x') # => '1, 2, w, x'
some_method('w', 'x', 'y') # => 'w, 2, x, y' #これがマズい結果になりやすい
some_method('w', 'x', 'y', 'z') # => 'w, x, y, z'
# 良好
def some_method(c, d, a = 1, b = 2)
puts "#{a}, #{b}, #{c}, #{d}"
end
some_method('w', 'x') # => '1, 2, w, x'
some_method('w', 'x', 'y') # => 'y, 2, w, x'
some_method('w', 'x', 'y', 'z') # => 'y, z, w, x'
なかなかわかりにくいサンプルですが、「良好」の方ではa = 1
とb = 2
というオプション引数を引数リストの末尾においていますので、引数リストは「c, d, a, b」といささか異様な順序になっています。
逆に「不可」の方ではsome_method('w', 'x') # => '1, 2, w, x'
のように、引数a
に”w”を渡したつもりがデフォルト値の1
が使われ、渡した”w”がc
に渡されるという挙動になっています。これは場合によっては望ましくない動作になると思います。
どちらもメソッド呼び出しで引数をすべて渡すと正常に動作するので、引数を減らしてみて初めて気づくということがありそうです。
この動作はRubyの仕様によるものであり、不具合を避けるためにこのスタイルを決めたと理解しました。
参考
以下は主にRubyのキーワード引数についての記事ですが、近年のRubyでよく変更されている機能なので、オプション引数について理解するときに合わせて読むとよいと思います。
多重代入は変数定義では避ける
Avoid the use of parallel assignment for defining variables. Parallel
assignment is allowed when it is the return of a method call, used with
the splat operator, or when used to swap variable assignment. Parallel
assignment is less readable than separate assignment.
多重代入自体はコードを簡潔に書くのに有用ですが、変数定義では避けるようにとの指示です。
たぶん多重代入だと変数定義らしく見えないからではないかと推測しています。変数が多くなったときにもわかりにくくなりそうですね。
多重代入は以下の場合に認められます。
- メソッドの返り値
- splat演算子としての
*
- 変数の値の入れ替え
# 不可
a, b, c, d = 'foo', 'bar', 'baz', 'foobar'
# 良好
a = 'foo'
b = 'bar'
c = 'baz'
d = 'foobar'
# 良好 - 変数の値入れ替えに使う場合
# Swapping variable assignment is a special case because it will allow you to
# swap the values that are assigned to each variable.
a = 'foo'
b = 'bar'
a, b = b, a
puts a # => 'bar'
puts b # => 'foo'
# 良好 - メソッドの返り値
def multi_return
[1, 2]
end
first, second = multi_return
# 良好 - splat演算子で使う場合
first, *list = [1, 2, 3, 4] # first => 1, list => [2, 3, 4]
hello_array = *'Hello' # => ["Hello"]
a = *(1..3) # => [1, 2, 3]
次回
今回はここまでにしたいと思います。次回はアンダースコア_
の項からです。ご期待ください。