読者です 読者をやめる 読者になる 読者になる

verbalize

文章書く練習.適当でいいから色々書きたい.

Rubyの文法のミニメモ

Railsでサービス作ってみたは良いものの、Rubyに関する理解が結構おろそかになっている。なので、今回は基本的だけど未だに理解できていない部分を簡単にまとめる。

Rubyはすべてがオブジェクト

Rubyに入門すると一度は耳にする「Ruby完全にオブジェクト指向的な言語である」という文言。入門したときはあまり深く考えていなかったので、よく考えると「いやいやPythonにだってクラスはあるんだからオブジェクト指向は使えるじゃん」とか思っていた。Pythonオブジェクト指向は後付けのものだということを聞いたことがあるので、まぁそんな程度の違いだろうという曖昧な理解だった。

しかし、改めて調べてみてRubyが完全にオブジェクト指向的であることが簡単にわかる例があったので書いておく。

# Rubyが完全にオブジェクト指向的であるというということは
# 1などの定値もオブジェクトになっているということ
p 1.class # => Fixnum

# 1がオブジェクトならmethodを持っているよね
p 1.methods # => [:%, :&, :*, :+, ・・・]

# +というメソッドがあるなら足し算はこう書ける
p 1.+(1) #=> 2

# 数字がオブジェクトのおかげでこういうRubyらしい書き方ができる
10.times {|i| print i} # => 0123456789

なるほど。これで前よりは少し理解が進んだ。

シンボルとハッシュ

Rubyにはシンボルという型がある。:(変数名)で定義でき、注意点は

:symbol == :"symbol" # => true

となること。

このシンボルであるが、よくハッシュで使われる。ハッシュはkeyとvalueで構成され、一般的にkeyは文字列で定義される。

しかし、keyを文字列として扱うと、valueを参照(keyの識別)する際にコストの高い文字列処理を行わなければならなくなる。ここでシンボルである。

端的にいうと、シンボルは任意の名前をつけることの出来る整数である。例えば:symbolというシンボルはなんらかの整数と紐付けられており、常に一定となっている。よってシンボルをkeyとすることで、文字列処理を行わなければ行けなかったところを、コストの低い整数処理に置き換えることが出来る。

わかりやすい例として、以下のようなコードを実行してみると、同じ文字列でも異なるオブジェクトidとなることがわかる。異なるオブジェクトなのだから当然といえば当然である。

a = "test"
b = "test"
a.equal?(b) # => false 

一方、シンボルはオブジェクトによらない。

a = :test
b = :test
a.equal?(b) # => true

ネットで調べると「Rubyのシンボルは文字列の皮を被った整数だ」という表現がされていて、的確だなと思った。文字列を内容のためではなく、一種の固有な識別子として使うコードを書こうとしているときにはシンボルのほうが適切である。

また、ここからは余談だが、Ruby1.9以降では

numbers = {"one" => 1, "two" => 2, "three" => 3}
numbers["one"] # => 1

numbers = {:one => 1, two => 2, three => 3}
numbers[:one] # => 1

のような標準的な記法に加え、

numbers = {one: 1, two: 2, three: 3}

という省略記法がある。この省略記法では自動的にkeyがシンボルになるので注意が必要である。

do, {}, () の使い分け

特にイテレータを使う時の使い分けが混同する。 例えば、

10.times {|i| p i}

10.times do |i|
  p i 
end

は同じ動きをする。一般的にはブロックの中身が1行のときは{}が、複数行となるときはdoが習慣的に使われるので覚えておく。ちなみにdo{はメソッド呼び出しと同じ行にあれば良いので、

# do endを一行で
10.times do |i| p i end

# {}を複数行で
10.times { |i|
  p i
}

と逆の形で書いても特にエラーにならないことも覚えておく。

また、doに関しては

text = File.open("./test.txt") do |f|
  filesize = f.stat.size
  f.read
end

のような形で使われることも度々あるが、単に最後に評価した値がtextに入るだけなので驚く必要はない。ブロックで区切られているので分かりやすいといえばそうかもしれない。

最後に、RSpecを使っていて、(){}を同様の役割で使っていて少し混乱した例があった。

specify { expect(1+1).to eq(2) }
specify { expect{1+1}.to eq(2) }

上記の2つはexpectの括弧が異なっているが同じ動作をする。なんなんだこれはと思い調べると、Procオブジェクトというものであることがわかった。

簡単に言うと、Procオブジェクトはブロック({|i| p i }など)をオブジェクト化したものである。深く理解したわけではないが雰囲気をつかむには以下のサイトが役に立った(古いけど)。

d.hatena.ne.jp

ではブロック(またはProcオブジェクト)を引数として渡すと何が嬉しいのか。これは推測だが、おそらく関数の引数に関数を渡すことが出来るという点でメリットがあるのだと思う。ついでに気づいたのだけれど、Rubyはすべてがオブジェクトであると言いつつも、ブロックや関数はオブジェクトではない気がする。だからこそ関数そのものをオブジェクト化できるProcにメリットがあるのではなかろうか。

上記のRSpecの例だと、おそらくexpect(1+1)では、expect(2)と同様の動作していて、expect{1+1}はそのまま{1+1}というブロックがexpect内で評価されて実行されるようにexpectが作られているのだと思う。

Procは要勉強。

改行して良いところ

これもあるあるだけど、書いていて長くなってしまった文を途中で改行したくなることがある。ただ、どこでなら改行していいのかわからなくなる。

基本的に改行は文の終端と判断されるので、改行する前の部分のみで評価が成立しまう文は改行できない(\でエスケープしてやる必要がある)。一方、評価が継続されるような場合は改行しても問題ない。例えば、

numbers = {
  one: 1
  two: 2
  three: 3
}

は問題ない。ただこれは結局わかりやすいルールとはいえなくて、慣れが必要だなと思う。

所感

まだ知っている言語が少ないせいかもしれないが、Ruby10.times10.uptoはとても見やすくて好き。do endPythonのインデントに比べるとネストが深くなった時に幾分見やすいように感じる。

ただRuby本当に独特なルールも多くて、メソッドの括弧を省略できるのが慣れない。というのも、サンプルコードをコピペしたりしていると、メソッドに対してそれがメソッドだと認識せずに使っていたりすることがある。 例えば

# uptoはメソッドで10が引数。前後の括弧は省略されている
1.upto 10 do |x|
    p x
end

# よってこういうことをするとエラーになる
1.upto 10 {|x| p x}

のように、エラーが出て初めて気づく例がある。また、括弧がないせいでメソッドと変数がパッと見分けられなかったりもする。メリットとしては、括弧を省略できると打つのが楽になる。括弧がないので引数の変更も楽になる。それぐらいかな…。

あとついでに、関数内で最後に評価された値が自動的に返されるのも慣れない。返り値を変えるために関数の最後で変数を呼ぶだけの行があったりすると苦笑してしまう。returnと書いたほうが良いと思う。

今後新たに気付きがあればまた書きたいなと思う。

プログラミング言語 Ruby

プログラミング言語 Ruby