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

[ブログ記事](http://secret-garden.hatenablog.com/entry/2015/12/01/204421)からの転載です。

## 本題

さて、わたしは元々 C++ 界隈の人間なのですが、最近は Ruby ばかり書いています。  
そこで、今回は C++er が Ruby を書いてみて驚いたことをちょっと紹介してみようかと思います。  
ちなみにわたしは Rails は触ったことはありません。  

## if や while, class, def などの制御構造はすべて式

Ruby でや if や while, class などの制御構造はすべて式になります。  
ですので、代入式の右辺などに記述する事ができます。

```ruby
flag = true
result = if flag
	"OK"
else
	"NG"
end
p result
# => "OK"
```

式であるのでメソッドの引数内でも `class` なんかを定義することができます。

## ブロックがある

Ruby の特徴的な構文にブロックというものがあります。  
これはメソッドの引数として渡すことができ、そのメソッドに対する操作を行うような処理を記述する事ができます。

```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 が省略された場合はその構文内の最後の式が戻り値として返されます。

```ruby
def plus a, b
	# return を省略して書くことができる
	a + b
	# return a + b
end


def check flag
	# if を戻り値として返す
	if flag
		"OK"
	else
		"NG"
	end
end
```

## メソッド呼び出し時に () を書かなくてもよい

Ruby ではメソッドの呼び出し時に `()` を省略することができます。  

```ruby
def plus a, b
	a + b
end

# 他の言語であれば () をつけてメソッドを呼び出すが
puts(plus(1, 2))

# Ruby では () を省略して記述する事ができる
puts plus 1, 2
```

これにより Ruby のメソッドをより DSL っぽく記述する事ができます。

## オープンクラスを拡張できる

Ruby では既存のクラスのメソッドなどあとから再定義する事ができます。  
これにより組み込みクラスの挙動などを変更することもできます。

```ruby
# 組み込みクラスである String に #twice メソッドを追加する
class String
	def twice
		self + sel
	end
end
p "homu".twice
# => "homuhomu"
```

## 演算子はメソッド呼び出しのシンタックスシュガー

Ruby では演算子はメソッド呼び出しのシンタックスシュガーとなります。

```ruby
# 1 + 2 は次のようなメソッド呼び出しのシンタックスシュガー
1.+(2) # => 3

# [] みたいなのも同じ
[1, 2, 3].[](1) # => 2
```

また、演算子がメソッドであるため、任意の演算子を再定義したい場合はメソッドとして定義することができます。

```ruby
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` となります。

```ruby
if 0
	p "OK"
else
	p "NG"
end
# => "OK"
```

ちなみに `"homu" =~ /^h/` という比較式は `0` が返ってくるので `false` と勘違いするが `true` である（これは戻り値が『マッチした位置』を返すため `0` が返ってくる

## クラスは型ではなくてオブジェクト

C++ ではクラスは型として扱われますが、Ruby ではクラスはオブジェクトとして扱われます。  
`class` が以下のように `Class` クラスのインスタンスオブジェクトとして定義されるとイメージしやすいと思います。

```ruby
# クラスを 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` メソッドは割と簡単に外部から呼び出すことができます。

```ruby
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` メソッドとして扱われます。

```ruby
# main オブジェクトの private メソッドとして定義される
def plus a, b
	a + b
end

# #send をかまして private メソッドを呼び出す
p self.send :plus, 1, 2
# => 3
```

また、次のようなコードを見てみると

```ruby
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 のメタプログラミングはスクリプト感を最大限に活かしてるような感じがして<del>闇っぽくて</del>結構好きです。  
また、Ruby には RubyGems があり、外部ライブラリを管理するのも簡単ですし充実してるので、やりたい事がサクッと実現する事ができますしねー。  
そんな感じで[Ruby Advent Calendar 2015](http://qiita.com/advent-calendar/2015/ruby)、1日目の記事でしたー。

## おまけ

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

* [Ruby でブロックを遅延評価するための gem を作った - Secret Garden(Instrumental)](http://secret-garden.hatenablog.com/entry/2015/04/14/224639)