Ruby初心者を脱した人が悩みがちな、ちょっと特殊な記法・演算子・イディオム

  • 237
    いいね
  • 2
    コメント

初心者がRubyの基礎を覚えたところで他人のコードを読むようになると、*argsmap(&:to_s) のような謎の記法がでてきます。
この手の記法は名前がわからないとググラビリティが低くなるため、人によってはつまづきとなることが多いようです。
尋ねられることがたまにありますので、この機会にRails開発でよく使われるものを中心にまとめてみます。

引数

*args

変数名の冒頭に*(アスタリスク)が付いているもので、「可変長引数」または「splat引数」とかいいます。
引数を複数個設定でき、さらにメソッド内部で引数を配列として受け取ることができます。

def splatter(*args)
  p args
end

splatter('foo')
# => ['foo']

splatter('foo', 'bar', 'baz')
# => ['foo', 'bar', 'baz']

なお、引数に*をつけると配列を引数として渡せます。

def math_consts(name, value)
  "#{name} = #{value}"
end

constants = [["π", Math::PI], ["e", Math::E]]

constants.each do |constant|
  puts math_consts(*constant)
end

#=> π = 3.141592653589793
#=> e = 2.718281828459045

なお、慣用的にargumentsを表す*argsと名付けられることが多いですけど、ただの変数名なので*constantsでも何でも大丈夫です。

**opts

**(アスタリスク2つ)も可変長引数ですが、こちらはキーワード引数をハッシュで受け取ることを表しています。
オプション設定が必要なメソッドで使われることが多いと思います。

def hello_with_option(msg, **opts)
  name = opts[:name]
  time = opts[:time] || Time.now
  puts "#{msg} #{name}@#{time}"
end

hello_with_option("Hello!", name: "nashirox")
# => Hello! nashirox@2017-02-07 16:58:46 +0900

デフォルト引数で空ハッシュ opts = {} を設定しても同じことになります。

&block

仮引数(例えばblock)に&(アンパサンド)を前置すると、「ブロック引数」となります。
メソッドはこの仮引数を介してブロックを受け取れるようになります。

def this_is_block(&block)
  block.call # yield でも同じ
end

this_is_block do
  p "This is my block"
end
# => "This is my block"

より詳細には、&を前置することでブロックをProcオブジェクト(手続きオブジェクト)に変換しています。

&:method_name

mapなどブロックで配列の中身を受け取るようなメソッドに、ブロック代わりに&を前置したメソッド名(:method_name)を渡すことができます。

# 配列の要素をstringにして返す
[1, 2, 3].map(&:to_s)
# => ["1", "2", "3"]

# 上と同じ
[1, 2, 3].map { |i| i.to_s }
# => ["1", "2", "3"]
# 配列内の総和を得る
[1, 2, 3].inject(&:+)
# => 6

なぜこう言ったことができるかについてはProcに関する理解が必要なのでここでは割愛します、調べると色々出てくると思います。

参考:Procを制する者がRubyを制す(嘘)

_

_(アンダースコア)を用いて、使わない変数名を宣言することができます。

sample.rb
fruits = [
  ['apple', 200], ['mango', 800], ['pineapple', 500]
]

p fruits.sort{ |(n1, p1), (n2, p2)| p2 <=> p1 }

このコードをwオプション付きで実行すると警告が出ます。

$ ruby -w sample.rb
#=> sample.rb:5: warning: assigned but unused variable - n1
#=> sample.rb:5: warning: assigned but unused variable - n2
#=> [["mango", 800], ["pineapple", 500], ["apple", 200]]

警告を避けるためには_を使うか、Ruby 2.0以降では_を変数名に前置するだけで良いです。

fruits = [
  ['apple', 200], ['mango', 800], ['pineapple', 500]
]

# Ruby 1.9以前
p fruits.sort{ |(_, p1), (_, p2)| p2 <=> p1 }
#=> [["mango", 800], ["pineapple", 500], ["apple", 200]]

# Ruby 2.0以降
p fruits.sort{ |(_n1, p1), (_n2, p2)| p2 <=> p1 }
#=> [["mango", 800], ["pineapple", 500], ["apple", 200]]
def hoge _, name
  puts _
  puts name
end

実際には警告を避けるというより、その変数が使われない(ないしは捨てている)ことを明示したいときに書くことが多いです。

演算子

||=

通称「nilガード」などと呼ばれ、初期化イディオムとして使われます。
左辺が真だったら何もせず、偽だったら右辺を代入します。

# 初期化されていない変数の呼び出し
foo
# => NameError: undefined local variable or method `foo' for main:Object


# fooが偽なので右辺が代入される
foo ||= true
foo
# => true

# fooが代入されて真となるので、右辺が代入されない
foo ||= 'hoge'
foo
# => true

<=>

人によって(?)は未確認飛行物体のように見えるようで、「宇宙船演算子」や「UFO演算子」と通称されます。
左辺と右辺を比較して、左辺側が大きければ正の整数、逆に小さければ負の整数を返します。双方等しければ 0、比較できなければ nil を返します。

100 <=> 200   # => -1
200 <=> 100   # =>  1
200 <=> 200   # =>  0
100 <=> '100' # => nil

なお自作クラスで比較演算したい場合、Comparable モジュールをクラスにインクルードし、<=> 演算子を定義することで可能になります。

class Human
  include Comparable
  attr_reader :age

  def initialize(name, age)
    @name = name
    @age  = age
  end

  def <=>(other)
    @age <=> other.age
  end
end

taro = Human.new("太郎", 25)
jiro = Human.new("次郎", 18)

p taro < jiro
# => false
p taro > jiro
# => true
p taro <=> jiro
# 1

->

ラムダ式で用いるリテラルで、「lambdaリテラル」や「アロー演算子」と呼ばれます。

# 定義
foo = -> (x) { x + x }
# 呼び出し
foo[5]   # => 10
foo.(5)  # => 10
foo.call 5 # => 10

# 通常のlambdaの書き方   
bar = lambda { |x| x + x }
bar.call 5 # => 10

Railsだとモデルのスコープで使われたりしていますね。

app/modes/comment.rb
class Comment < ActiveRecord::Base
  scope :created_before, ->(date) { where("created_at < ?", date) }
end

Hoge::Foo

::は「スコープ演算子」と呼ばれます。この演算子を利用して、あるクラスまたはモジュールで定義された定数を外部から参照することができます。
Railsの定数管理の手法として、initializers下にアプリケーション全体で使う定数を配置する方法などがありますね。

config/initializers/constants.rb
module Constants
  AgeStart = 0
  AgeEnd   = 99
end

Constantsモジュールで登録可能年齢の上限と下限を定義して、モデルで呼び出します。

app/modles/customer.rb
class Customer < ActiveRecord::Base
  age_start = Constants::AgeStart #=> 0
  age_end   = Constants::AgeEnd   #=> 99
  validates :age, :inclusion => { :in => age_start..age_end }
end

なお、左辺無しの::Fooと書けたりもして、これはトップレベルの定数を呼び出しています。

class Object
  # Objectクラスに定数Fooを追加
  Foo = 'foo'
end

::Foo
# => "foo"

&.

Ruby2.3から導入された、obj&.my_method形式でのメソッド呼び出しです。呼称はSafe navigation operator、通称ぼっち演算子です
objがnilでなかったらmy_methodを実行します。
Rails使いはActiveSupportのtry!をよく使うと思いますがあれと一緒で、言語の方がパクった取り入れたようです。

# railsだと今までこう書いていた
user.try!(:profile).try!(:sns).try!(:twitter)

# オブジェクトがnilならnilが返る
something_nil&.profile&.sns&.twitter
#=> nil

# nilでないオブジェクトでも存在しないメソッドを呼ぶとNoMethodError
building&.profile&.sns&.twitter
#=> NoMethodError: undefined method `profile?' for "building":Building

うまく活用すれば、呼び出し元がnilかも知れないと恐れる必要がなくありますね!
メソッド呼び出しは全部こいつでやれば安全

!!

すべてをtrueかfalseにします。
nilではなく、必ずfalseで返したい場合などに使います。
(なお、エクスクラメーションマークが一つだと否定演算子ですね)

!!nil   #=> false
!!"abc" #=> true
!!false #=> false

どういうところに使うのかと、Railsだと次のよう使い方があるようです。

def logged_in?
  !!session[:user_id]
end

呼び方ですが「二重否定」、欧米では「double-bang」や「bang-bang」などと言うようです(初めて知った)。

組み込み変数

$で始まる変数はグローバル変数ですが、その中でも特別な役割が与えられている特殊な変数が「組み込み変数」です。
似たような記号が多くて覚えづらいのですが、最低限3つを押さえておけば良いと思います。

$0

現在実行中のRubyスクリプトの名前が入った変数です。

sample.rb
class Sample
  def show_file_name
    puts "This file name is #{$0}"
  end
end

Sample.new.show_file_name
$ ruby sample.rb
# => This file name is sample.rb

$1, $2, $3..$n

最後に成功したパターンマッチでn番目の括弧にマッチした文字列が入っています。
最初の頃、$0も仲間かと思ってたけど全然違う。

"foo bar baz" =~ /(f\w+)\s(\w+)/
p $1  #=> "foo"
p $2  #=> "bar"
p $3  #=> nil

$&

最後に成功した正規表現のパターンマッチでマッチした文字列が入っています。

"foo bar baz" =~ /(f\w+)\s(\w+)/
p $& #=> "foo bar"

組み込み定数

ENV

Rubyで標準で用意されている組み込み定数がいくつかありますが、Railsの開発でよく用いるのはENV(エンブ)のみです。
ENVは環境変数を表すオブジェクトで、ENV['KEY_NAME']のような形でKEY_NAMEと合致する値を取り出すことができます。
Railsではsecret.ymlの中でENV["SECRET_KEY_BASE"]のように環境変数を取り出しています。

config/secrets.yml
development:
  secret_key_base: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

test:
  secret_key_base: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

環境変数をどう設定するかは割愛します。

その他記法

%w( )、%i( )、%s( )など

「パーセント記法」と呼ばれ、配列作成などによく使われます。
解説は各所に上がっていますので、詳細は別記事などを参照してください

# %w 文字列配列
fruits = %w(apple orange mango)
# => ['apple', 'orange', 'mango']
# %i シンボル配列
fruits = %i(apple orange mango)
# => [:apple, :orange, :mango]

なお、文字列配列は%記法を用いて書くことが推奨されています

=>

「ハッシュロケット」と呼ばれ、ハッシュリテラルを書く際にキーと値を区切るものです。
なお、Ruby 1.9以降では別の書き方が出てきて、現在ではそちらが推奨です。ハッシュロケットはできるだけ使わない方ようにしてください。

# Ruby 1.8まで
{ "name" => "Alex" }
{ :name => "Alex" }

# Ruby 1.9 以降
{ name: "Alex" }

なお、ハッシュリテラルのキーがシンボルで、., -等を含む場合、ハッシュロケットに統一するのが望ましいという考え方もあるようです。

# good
{ :cookpad => 42,
  :'cookpad.com' => 'foo',
}
# bad
{ cookpad: 42,
  :'cookpad.com' => 'foo',
}

以上です。なにか足すようなものがあったら追加します。
それではよきRubyライフを!