こんにちは、hachi8833です。
「Rubyスタイルガイドを読む」シリーズ、そろそろ文法編も終わりかと思いきやもう少し続きます。今回はlambda周りです。
文法(7) lambda、標準入出力など
2-47【統一】Rubyインタプリタは常に-w
オプションをつけて実行すること
Always run the Ruby interpreter with the
-w
option so it will warn you if you forget either of the rules above!
Rubyには他の実行系と同様に冗長モード(verbose mode)というものがありますが、-w
はバージョン情報などを除いた警告メッセージを表示します(参考: Rubyリファレンスマニュアル)。
-w
は-v
のようなわかりきったバージョン情報を表示しません。
2-48【統一】メソッド定義はネストしないこと(ネスト定義が必要ならlambdaを使う)
Do not use nested method definitions, use lambda instead.
Nested method definitions actually produce methods in the same scope(e.g. class) as the outer method. Furthermore, the "nested method" will be redefined every time the method containing its definition is invoked.
- 理由1: メソッド定義はネストされていてもいなくても、生成されるときのスコープは変わらない
- 理由2: ネストされたメソッドは、それを含むメソッドが呼び出されるたびに再定義されてしまう
# 不可
def foo(x)
def bar(y)
# (略)
end
bar(x)
end
# 良好 - スコープは上と同じだが、#fooが呼び出されるたびに#barが再定義されずに済む
def bar(y)
#(略)
end
def foo(x)
bar(x)
end
# これでもよい
def foo(x)
bar = ->(y) { 略 } # lambdaを使用
bar.call(x)
end
解説
メソッド定義をネストしてもメソッドの名前空間は別にならない、つまりA::hoge::huga
のようなものにはならずにA::hoge
とA::huga
になるということですね。
morimorihogeさんが書いてくれた以下のコード例で動作を確認できます。
class A
def hoge
def huga
end
end
end
a = A.new
a.huga # NoMethodError
a.hoge # 動く
a.huga # 動く。一行手前のhogeで定義されたhugaが実行される
a.hoge
a.huga # 動く。二行前のhugaとは別の再定義されたhugaが実行される
# ↑#hogeを呼び出す度にhugaが再定義される
A#huga
は、A#hoge
を一度実行するまでは未定義なのでエラーになり、A#huga
はA#hoge
を実行するたびに再定義されています。
kazzさんによると、Javaではそもそもメソッド定義のネストはできないそうです。また、lambda
が推奨されているのは、メソッド名を定義しないのでメソッド名の無用な衝突を避けられるからではないかとのことでした。
2-49【統一】 ブロックが1行のみなら、lambdaの略記->()
を使い、ブロックが複数行なら略記でないlambda
メソッドを使う
Use the new lambda literal syntax for single line body blocks. Use the
lambda
method for multi-line blocks.
これも例によって「どちらが正しい」ではなく、どちらに統一するかという問題ですね。
# 不可
l = lambda { |a, b| a + b } # 1行ブロックではlambdaメソッドは使わない
l.call(1, 2)
# 間違ってはいないが、->()にdo〜endブロックを続けると非常に読みづらい
l = ->(a, b) do
tmp = a * 7
tmp * b / 50
end
# 良好
l = ->(a, b) { a + b } # 1行ブロックなら略記->()と{ }ブロックで
l.call(1, 2)
l = lambda do |a, b| # 複数行ブロックならlambdaとdo〜endブロックで
tmp = a * 7
tmp * b / 50
end
ブロックの使い分けについては、Rubyスタイルガイドを読む: 文法(5)で扱った以下のルールに従っています。
ブロック記法: 1行に収まる場合は原則{...}を使い、制御フローやメソッド定義では常にdo...endを使う
2-50【統一】lambdaの略記->() { }
でパラメータがある場合は丸かっこ( )
を省略しないこと
Don't omit the parameter parentheses when defining a stabby lambda with parameters.
# 不可
l = ->x, y { something(x, y) }
# 良好
l = ->(x, y) { something(x, y) }
->x,
とy
が別物に見えてしまいそうなので、丸かっこが必要なのは納得です。
2-51【統一】lambdaの略記->() { }
でパラメータがない場合は丸かっこ( )
を省略すること
Omit the parameter parentheses when defining a stabby lambda with no parameters.
上と対をなすルールですね。読みやすさというよりタイプ量を減らすのが目的のようにも思えます。
# 不可
l = ->() { something }
# 良好
l = -> { something }
2-52【統一】 Proc.new
は避け、proc
を使う
Prefer
proc
overProc.new
.
# 不可
p = Proc.new { |n| puts n }
# 良好
p = proc { |n| puts n }
以下で説明されているようにProc.new
と#proc
に違いはないので、純粋にスタイル上の理由で#proc
にしたということですね。
Kernel.#proc は Proc.new と同じになります。 引数に & を付けることで手続きオブジェクト化したブロックは、Proc.new で生成されたそれと 同じにように振る舞います。
Rubyリファレンスマニュアルより
2-53【統一】lambdaやprocの実行にはproc.call()
を使う: proc[]
やproc.()
は避ける
Prefer
proc.call()
overproc[]
orproc.()
for both lambdas and procs.
# 不可
l = ->(v) { puts v }
l[1] # enumerationのアクセスに見えるのでよくない
# これも不可
l = ->(v) { puts v }
l.(1) # マイナーすぎる
# 良好
l = ->(v) { puts v }
l.call(1) # この記法がおすすめ
proc[]
やproc.()
で呼べるということ自体、初めて知りました。
2-54【統一】 使わないブロックパラメータ名やローカル変数名の冒頭には_
を付ける(_
のみでもよい)
Prefix with
_
unused block parameters and local variables. It's also acceptable to use just_
(although it's a bit less descriptive). This convention is recognized by the Ruby interpreter and tools like RuboCop and will suppress their unused variable warnings.
多重代入の変数や仮引数で使わない変数は、_
変数か、_
で始まる変数名にするのがRubyのスタイルです。こうすることで、その変数がその後使われないことを示せます。
Ruby実行系やRuboCop(文法チェック)は、_
変数や、_
で始まる変数については「変数が未使用」警告を表示しません。
# 不可
result = hash.map { |k, v| v + 1 }
def something(x)
unused_var, used_var = something_else(x)
# (略)
end
# 良好
result = hash.map { |_k, v| v + 1 }
def something(x)
_unused_var, used_var = something_else(x) # _unused_varは以後使われないことがわかる
# (略)
end
# 良好
result = hash.map { |_, v| v + 1 }
def something(x)
_, used_var = something_else(x) # _は以後使われないことがわかる
# (略)
end
2-55【統一】標準入出力は$stdout/$stderr/$stdin
に統一する
Use
$stdout/$stderr/$stdin
instead ofSTDOUT/STDERR/STDIN
.
STDOUT/STDERR/STDIN
are constants, and while you can actually reassign (possibly to redirect some stream) constants in Ruby, you'll get an interpreter warning if you do so.
STDOUT/STDERR/STDIN
は定数なので、再代入するとRubyで警告が表示されます。
2-56【統一】$stderr.puts
は避け、#warn
を使うこと
Use
warn
instead of$stderr.puts
. Apart from being more concise and clear,warn
allows you to suppress warnings if you need to (by setting the warn level to 0 via-W0
).
#warn
にしておくことで、Rubyの起動オプションで警告レベルを設定できるようになるからだそうです。たとえば-W0
オプションをつけてRubyを実行すると、警告レベルがゼロになり、警告が表示されなくなります。
2-57【統一】String#%
は避け、sprintf
またはformat
を使うこと
Favor the use of
sprintf
and its aliasformat
over the fairly crypticString#%
method.
format
はsprintf
の別名だそうです。
# 不可
'%d %d' % [20, 10]
# => '20 10'
# 良好
sprintf('%d %d', 20, 10)
# => '20 10'
# 良好
sprintf('%{first} %{second}', first: 20, second: 10)
# => '20 10'
format('%d %d', 20, 10)
# => '20 10'
# 良好
format('%{first} %{second}', first: 20, second: 10)
# => '20 10'
Strings#%」は、書式がシンプルな場合以外は避けるほうがよさそうです。
今回はここまでとします。次回の文法編は#join
などのメソッドです。ご期待ください。
関連記事
- Rubyスタイルガイドを読む: ソースコードレイアウト(1)エンコード、クラス定義、スペース
- Rubyスタイルガイドを読む: ソースコードレイアウト(2)インデント、記号
- Rubyスタイルガイドを読む: 文法(1)メソッド定義、引数、多重代入
- Rubyスタイルガイドを読む: 文法(2)アンダースコア、多重代入、三項演算子、if/unless
- Rubyスタイルガイドを読む: 文法(3)演算子とif/unless
- Rubyスタイルガイドを読む: 文法(4)ループ
- Rubyスタイルガイドを読む: 文法(5)ブロック、proc
- Rubyスタイルガイドを読む: 文法(6)演算子など
- Rubyスタイルガイドを読む: 文法(7)lambda、標準入出力など
- Rubyスタイルガイドを読む: 文法(8)配列や論理値など
- Rubyスタイルガイドを読む: 命名
- Rubyスタイルガイドを読む: コメント、アノテーション、マジックコメント
- Rubyスタイルガイドを読む: クラスとモジュール(1)構造
- Rubyスタイルガイドを読む: クラスとモジュール(2)クラス設計・アクセサ・ダックタイピングなど
- Rubyスタイルガイドを読む: クラスとモジュール(3)クラスメソッド、スコープ、エイリアスなど
- Rubyスタイルガイドを読む: 例外処理
- Rubyスタイルガイドを読む: コレクション(Array、Hash、Setなど)
- Rubyスタイルガイドを読む: 数値、文字列、日時(日付・時刻・時間)
- Rubyスタイルガイドを読む: 正規表現、%リテラル、メタプログラミング(最終回)