Help us understand the problem. What is going on with this article?

C++er が Ruby を書いてみて驚いた10のこと

More than 3 years have passed since last update.

ブログ記事からの転載です。

本題

さて、わたしは元々 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 では nilfalse 以外はすべて true になります。

なので C++ とは違い数値の 0true となります。

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

requireinclude, attr_accessor, private などは言語キーワードのように見えますが、Ruby ではこれらはすべてメソッドとして定義されています。

このように Ruby ではたいていの構文はメソッドを扱うような感じで記述していきます。

まとめ

と、言う感じでざっくりとまとめてみましたー。

やっぱり静的型付け言語の C++ と比べると Ruby は恐ろしく緩い、というか C++ とは違った意味でやりたい放題という感じがひしひしと伝わってきます。

今だと Ruby のメタプログラミングはスクリプト感を最大限に活かしてるような感じがして闇っぽくて結構好きです。

また、Ruby には RubyGems があり、外部ライブラリを管理するのも簡単ですし充実してるので、やりたい事がサクッと実現する事ができますしねー。

そんな感じでRuby Advent Calendar 2015、1日目の記事でしたー。

おまけ

以前、Ruby で便利ライブラリをつくったので気になる方は見てみてくださいー。

pink_bangbi
C++ とか Vim とか Ruby とかメタプログラミングとか
http://secret-garden.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away