【Ruby Advent Calendar 2015 1日目】C++er が Ruby を書いてみて驚いた10のこと
Ruby Advent Calendar 2015 とは
Ruby について何か書くアドベントカレンダーになります。
現在は Qiita で開催しています。
まだ参加者は募集中なので気になる方はぜひ参加してみてください。
本題
さて、わたしは元々 C++ 界隈の人間なのですが、最近は Ruby ばかり書いています。
そこで、今回は C++er が Ruby を書いてみて驚いたことをちょっと紹介してみようかと思います。
ちなみにわたしは Rails は触ったことはありません。
if や while, class, def などの制御構造はすべて式
Ruby でや if や while, class などの制御構造はすべて式になります。
ですので、代入式の右辺などに記述する事ができます。
flag = true result = if flag "OK" else "NG" end p result # => "OK"
式であるのでメソッドの引数内でも class
なんかを定義することができます。
ブロックがある
Ruby の特徴的な構文にブロックというものがあります。
これはメソッドの引数として渡すことができ、そのメソッドに対する操作を行うような処理を記述する事ができます。
ary = [1, 2, 3, 4, 5] # 各要素を2倍にする ary.map { |it| it + it } # => [2, 4, 6, 8, 10] # 偶数を選ぶ p ary.select { |it| it % 2 == 0 } # => [2, 4]
このように配列を操作するような場合などに利用することができます。
C++ でも最近はラムダ式が導入されて似たような記述をすることができますが、Ruby では言語レベルでこういう機能が導入されています。
return を省略できる
Ruby では return を省略して書くことができます。
return が省略された場合はその構文内の最後の式が戻り値として返されます。
def plus a, b # return を省略して書くことができる a + b # return a + b end def check flag # if を戻り値として返す if flag "OK" else "NG" end end
メソッド呼び出し時に () を書かなくてもよい
Ruby ではメソッドの呼び出し時に ()
を省略することができます。
def plus a, b a + b end # 他の言語であれば () をつけてメソッドを呼び出すが puts(plus(1, 2)) # Ruby では () を省略して記述する事ができる puts plus 1, 2
これにより Ruby のメソッドをより DSL っぽく記述する事ができます。
オープンクラスを拡張できる
Ruby では既存のクラスのメソッドなどあとから再定義する事ができます。
これにより組み込みクラスの挙動などを変更することもできます。
# 組み込みクラスである String に #twice メソッドを追加する class String def twice self + sel end end p "homu".twice # => "homuhomu"
演算子はメソッド呼び出しのシンタックスシュガー
Ruby では演算子はメソッド呼び出しのシンタックスシュガーとなります。
# 1 + 2 は次のようなメソッド呼び出しのシンタックスシュガー 1.+(2) # => 3 # [] みたいなのも同じ [1, 2, 3].[](1) # => 2
また、演算子がメソッドであるため、任意の演算子を再定義したい場合はメソッドとして定義することができます。
class X def initialize @name = "homu" end def + other @name + other end end p X.new + "mami" # => "homumami"
ただし ||
や &&
などの一部の演算子は言語の組み込み演算子になるため、メソッドとして扱うことはできません。
nil と false 以外は true
Ruby では nil
と false
以外はすべて true
になります。
なので C++ とは違い数値の 0
も true
となります。
if 0 p "OK" else p "NG" end # => "OK"
ちなみに "homu" =~ /^h/
という比較式は 0
が返ってくるので false
と勘違いするが true
である(これは戻り値が『マッチした位置』を返すため 0
が返ってくる
クラスは型ではなくてオブジェクト
C++ ではクラスは型として扱われますが、Ruby ではクラスはオブジェクトとして扱われます。
class
が以下のように Class
クラスのインスタンスオブジェクトとして定義されるとイメージしやすいと思います。
# クラスを Class クラスのインスタンスオブジェクトとして定義する X = Class.new { attr_accessor :name def + other name + other end } x = X.new x.name = "homu" p x + "mami" # => "homumami"
このように Ruby ではクラスは型ではなくて Class クラスのオブジェクトとして使われます。
private メソッドが自クラス以外からも呼びだせる
C++ の private
は基本的には自クラス内でしか呼び出すことはできないんですが、Ruby の private
メソッドは割と簡単に外部から呼び出すことができます。
class X private def name "homu" end end x = X.new # レシーバ付きでメソッドを呼び出すことができない # x.name # Error: private method `name' called for #<X:0x00000001d76c10> # 逆に言えばレシーバを付けなければ呼び出すことができる p x.send(:name) # => "homu" p x.instance_eval { name } # => "homu"
Ruby の private
メソッドはレシーバを付けて呼び出せなくなるだけなので、それ以外の手段(#send
や #instance_eval
など)を利用すれば private
メソッドも呼び出すことができます(ちなみに self.name
という呼び出し方はレシーバを付けてるためエラーになります。
ほとんどがメソッドで構成されてる
Ruby では関数は存在せずにほとんどがメソッドとして扱われます。
例えば、次のようにトップレベルにメソッドを定義した場合もグローバル関数ではなくて main
オブジェクトの private
メソッドとして扱われます。
# main オブジェクトの private メソッドとして定義される def plus a, b a + b end # #send をかまして private メソッドを呼び出す p self.send :plus, 1, 2 # => 3
また、次のようなコードを見てみると
require "json" class X include Math attr_accessor :name private def add other name << other end end
require
や include
, attr_accessor
, private
などは言語キーワードのように見えますが、Ruby ではこれらはすべてメソッドとして定義されています。
このように Ruby ではたいていの構文はメソッドを扱うような感じで記述していきます。
まとめ
と、言う感じでざっくりとまとめてみましたー。
やっぱり静的型付け言語の C++ と比べると Ruby は恐ろしく緩い、というか C++ とは違った意味でやりたい放題という感じがひしひしと伝わってきます。
今だと Ruby のメタプログラミングはスクリプト感を最大限に活かしてるような感じがして闇っぽくて結構好きです。
また、Ruby には RubyGems があり、外部ライブラリを管理するのも簡単ですし充実してるので、やりたい事がサクッと実現する事ができますしねー。
そんな感じでRuby Advent Calendar 2015、1日目の記事でしたー。
おまけ
以前、Ruby で便利ライブラリをつくったので気になる方は見てみてくださいー。