Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rubyスタイルガイドを読む: 文法(6)演算子など

こんにちは、hachi8833です。

「Rubyスタイルガイドを読む」シリーズ文法編、第6回目をお送りします。

文法(6) 演算子など

2-38【統一】分岐の条件部分で=による代入の結果を値として使う場合は、条件全体をかっこ( )で囲むこと

Don't use the return value of = (an assignment) in conditional expressions unless the assignment is wrapped in parentheses. This is a fairly popular idiom among Rubyists that's sometimes referred to as safe assignment in condition.

# 不可 (+ a warning)
if v = array.grep(/foo/)
  do_something(v)
  # コード
end

# 良好 (MRIでは警告されるかもしれないが、RuboCopでは警告されない)
if (v = array.grep(/foo/))
  do_something(v)
  # コード
end

# 良好
v = array.grep(/foo/)
if v
  do_something(v)
  # コード
end

代入=の結果を条件式に使うことについて、「==と間違えたように見えて気持ち悪いから嫌だ」という意見と「いや、=を条件に書く方が簡潔でいいじゃないか」という意見がよく衝突します。「代入の結果を使う場合は条件全体をかっこ( )で囲む」というのは、おそらく両者の間を取ったのでしょう。

なお、私もときどきわからなくなっていましたが、代入演算子=が返す結果は「true/false」ではなく「代入された値」です。

2-39【統一】できるだけ自己代入演算子を使う

Use shorthand self assignment operators whenever applicable.

# 不可
x = x + y
x = x * y
x = x**y
x = x / y
x = x || y
x = x && y

# 良好
x += y
x *= y
x **= y
x /= y
x ||= y
x &&= y

自己代入演算子を使わないスタイルはそれはそれで読みやすいというメリットもあると思いますが、これもどちらが正しいとかではなく、自己代入演算子を使う方に揃えたということですね。

2-40【統一】 「変数が初期化されていない場合のみ初期化する」場合は、||=を使う

Use ||= to initialize variables only if they're not already initialized.

# 不可
name = name ? name : 'Bozhidar'

# 不可
name = 'Bozhidar' unless name

# 良好 - nilかfalseの場合のみ'Bozhidar'が代入される
name ||= 'Bozhidar'

三項演算子や後置のif/unlessではなく、最も短く簡潔に書ける||=に揃えるのは納得です。

2-41【統一】論理値の初期化には||=を使わないこと

Don't use ||= to initialize boolean variables. (Consider what would happen if the current value happened to be false.)

1つ上のスタイルの例外ということになります。Rubyではnilとfalseの論理値が同じであるため、||=での初期化には向いていませんね。

# 不可 - enabledの値がfalseであってもtrueになってしまう
enabled ||= true

# 良好
enabled = true if enabled.nil?

2-42【統一】「その変数が存在する場合のみ値を代入する」 処理には&&=を使う

Use &&= to preprocess variables that may or may not exist. Using &&= will change the value only if it exists, removing the need to check its existence with if.

# 不可
if something
  something = something.downcase
end

# 不可
something = something ? something.downcase : nil

# ok
something = something.downcase if something

# 良好
something = something && something.downcase

# better
something &&= something.downcase

&&=ならif/unlessや三項演算子が不要になるので、これも納得です。

2-43【統一】===はなるべく避ける

Avoid explicit use of the case equality operator ===. As its name implies it is meant to be used implicitly by case expressions and outside of them it yields some pretty confusing code.

# 不可
Array === something
(1..100) === 7
/something/ === some_string

# 良好
something.is_a?(Array)
(1..100).include?(7)
some_string.match?(/something/)

#640の変更を反映しました。

この間TechRachoで取り上げたばかりの===演算子ですが、ここではcase equality operatorと呼んでおり、その名のとおりcase文で暗黙に使われるのが本来なのでそれ以外の場所で使うべきではないとのことです。

case equalityは大文字小文字を区別するのかと思ってしまいましたが、違いました。

case は一つの式に対する一致判定による分岐を行います。when 節で指定された値と最初の式を評価した結果とを演算子 === を用いて 比較して、一致する場合には when 節の本体を評価します。
Rubyリファレンスマニュアル: caseより

2-44【統一】eql?は必要ない限り使わない: 極力==を使う

Do not use eql? when using == will do. The stricter comparison semantics provided by eql? are rarely needed in practice.

# 不可 - eql? は文字列比較では == と同じ
'ruby'.eql? some_str

# 良好
'ruby' == some_str
1.0.eql? x # これは許容される: eql?はIntegerの1とFloatの1を区別するのに必要

#eqlは上のようにやや厳密な比較を行いますが、「現実にはeql?はめったに使わない」だそうです。

オブジェクトと other が等しければ真を返します。Hash で二つのキー が等しいかどうかを判定するのに使われます。
このメソッドは各クラスの性質に合わせて再定義すべきです。 多くの場合、 == と同様に同値性の判定をするように再定義されていますが、 適切にキー判定ができるようにより厳しくなっている場合もあります。
デフォルトでは equal? と同じオブジェクト の同一性判定になっています。
このメソッドを再定義した時には Object#hash メソッ ドも再定義しなければなりません。
Rubyリファレンスマニュアル: Object#eql?より

2-45【統一】Perl由来の特殊変数($:$;など)は極力避けること

Avoid using Perl-style special variables (like $:, $;, etc. ). They are quite cryptic and their use in anything but one-liner scripts is discouraged.
Use the human-friendly aliases provided by the English library.

# 不可
$:.unshift File.dirname(__FILE__)

# 良好
require 'English'
$LOAD_PATH.unshift File.dirname(__FILE__)

個人的にも$で始まる変数群はとてもわかりにくいうえに、目にイガイガして残念だと思っているので、このスタイルには賛成です。

たとえば$&なら#last_matchにしたいところです。

/(.)(.)/ =~ "ab"
p Regexp.last_match      # => #<MatchData:0x4599e58>
p Regexp.last_match(0)   # => "ab"
p Regexp.last_match(1)   # => "a"
p Regexp.last_match(2)   # => "b"
p Regexp.last_match(3)   # => nil

Rubyの特殊変数とよりよい表記の一覧については、次のTechRacho記事をご覧ください。

2-46【統一】メソッド名と開きかっこ( の間にはスペースを置かない

Do not put a space between a method name and the opening parenthesis.

# 不可
f (3 + 2) + 1

# 良好
f(3 + 2) + 1

スペースに関するスタイルはソースコードレイアウトに置いて欲しいですね(´・ω・`)。

追伸

虎塚さんの記事を読んでいてたまたま気づいたのですが、今回扱ったRubyのコーディングスタイルは『Effective Ruby』にも同じ指摘があるようです。

[tmkm-amazon]4798139823[/tmkm-amazon]


関連記事



CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。