PHPer向けのRuby入門

## はじめに

* この記事は`Ruby`の書き方・読み方を最低限説明するために作成されたものである。
* `PHP`がある程度普通に触れている人向けに、`Ruby`ではどう書く？みたいなところが中心
* `Ruby`の環境は既に整ってる前提

## 基本思想

### `PHP`は関数が基本

* 例えばある配列から先頭の要素を取り出す時

```php
$a = array(1,2,3);
$first = array_shift($a);
var_dump($first);
var_dump($a);
```

* `array_shift`という関数に引数を渡すことで任意の処理を行う。

### `Ruby`はオブジェクトが基本

* 同様に配列から先頭の要素を取り出す時

```rb
a = [1,2,3]
first = a.shift
p first
p a
```

`Array`オブジェクト`a`の`shift`メソッドで先頭の要素を取り出す。

これが`PHP`と`Ruby`の基本思想の違いである。

## まず先に出力方法

* 基本的な出力形式を押さえておく。
* `PHP`でいう「困ったらダンプしてみる」というのは`Ruby`でも通用するので`p`メソッドを覚えておくと何かと便利。
* コメント文は`#`

| PHP             | Ruby       | 
|:----------------|:-----------|
|echo('hoge');    |print 'hoge'|
|echo("hoge¥n");  |puts 'hoge' |
|var_dump('hoge');|p 'hoge'    |
|//コメント文       |#コメント文  |

## 実際に出力してみよう

コンソールで`irb`と打つとインタラクティブシェルが起動する。まずはファイルを作成せずにシェルで挙動を確認しましょう。

| PHP  | Ruby| 
|:-----|:----|
|php -a|irb  |

### 演習問題1
インタラクティブシェルで`print`, `puts`, `p`の出力を試してみましょう。

## 拡張子と実行方法

プログラムファイルの拡張子は`.rb`、実行は`ruby`コマンド

| PHP        | Ruby        | 
|:-----------|:------------|
|.php        |.rb          |
|php hoge.php|ruby hoge.rb |

```php:hoge.php
<?php
$a = 'hoge';
var_dump($a);
```

```rb:hoge.rb
a = 'hoge'
p a
```

* `<?php`等の開始タブが不要
* 変数に`$`不要
* 行末にセミコロン不要（つけても良い）
* カッコの省略可（つけても良い）

### 演習問題2
実際に上記ファイルを作って実行してみましょう。

## ループ

```php:loop.php
<?php
for($i=0; $i<10; $i++){
    echo($i."\n");
}
```

```rb:loop.rb
for i in 0...10 do
  p i
end
```

ここで覚えて欲しいのは`0...10`の範囲オブジェクト
`0..10`だと10を含む、`0...10`だと10を含まない。
また`PHP`で中括弧で囲んでいる部分はそのまま`Ruby`では`do ~ end`で置き換えることができる。

ちなみに上記の`for`文はほぼ使わない。
私も久しぶりに使うにあたってドキュメントを参照しながらでないと書けなかった（汗
上記の例でいうと範囲オブジェクトに対して`each`メソッドを使って

```rb:loop2.rb
(0...10).each do |i|
  p i
end
```

こんな感じに書くのが自然である。
もっと言うと基本は配列等のオブジェクトに対してループを回すことが大半である（`PHP`でいうところの`foreach`と同義）
そのあたりは次の配列の項で触れる。

### 演習問題3
1から100までの偶数を出力して下さい。


## 配列

```php:array.php
<?php
$a = array('Hello', 'new', 'world');
foreach($a as $value){
    var_dump($value);
}
```

```rb:array.rb
a = ['Hello', 'new', 'world']
a.each do |value|
  p value
end
```
これが前項で述べていたオブジェクト（この場合には配列）に対して`each`でループを回している様子である。`foreach`だと思えば馴染みやすいかと思う。
`Array`オブジェクトに色々なメソッドがあるので適宜使って欲しい。

> http://ref.xaio.jp/ruby/classes/array

| PHP            | Ruby   | 
|:---------------|:-------|
|sort($a)        |a.sort  |
|empty($a)       |a.empty?|
|array_unique($a)|a.uniq  |
|count($a)       |a.count |

ここで出てきたのでついでに紹介するが`Ruby`ではメソッド名に`!`と`?`が使用できる。
自由に命名に使って良いが慣習として

* `true` or `false`の`boolean`を返すメソッドには末尾に`?`
* 対象のオブジェクトに対して破壊的なメソッドには末尾に`!`を使う。

例えば`array.uniq`は`array.uniq!`という元の配列を上書き（破壊）してしまうものも存在する。

### 演習問題4
次の処理の`PHP`を`Ruby`に書き換えてみましょう。

```php:example4.php
<?php
// カンマ区切りの文字列を配列に格納
$string = 'Hello, new, world';
$array = explode(', ', $string);
print_r($array);

// 配列の各要素をカンマ区切りの文字列に変換
$convert_string = implode(', ', $array);
print_r($convert_string);

// カンマ区切りの文字列をコロン区切りの文字列にする
print_r(str_replace(',', ':', $convert_string));
```

## 条件分岐

```php:if.php
<?php
$a = 'hoge';
if($a === 'hoge'){
    echo("true\n");
}
```

```rb:if.rb
a = 'hoge'
if a == 'hoge'
  p 'true'
end
```

特に難しいことはない。

* カッコの省略
* 中括弧が`then ~ end` (`then`は省略可)
* `==`で`PHP`の`===`と同じく厳密比較

`Ruby`ならではの書き方を以下に記載する。

### unless

```php:unless.php
<?php
$a = 'hoge';
if($a !== 'moge'){
    echo('true');
}
```

```rb:unless.rb
a = 'hoge'
unless a == 'moge'
  p 'true'
end
```

`unless`とは簡単に言えば`!==`と同義である。
簡単に否定の比較ができるので割と重宝する。
`!`よりも見落としにくい。

### elsif

```php:else.php
<?php
$a = 'hoge';
if($a === 'moge'){
    echo('moge');
} else if($a === 'hoge'){
    echo('hoge');
} else{
    echo('else');
}
```

```rb:else.rb
a = 'hoge'
if a == 'moge'
  p 'moge'
elsif a == 'hoge'
  p 'hoge'
else
  p 'else'
end
```

`else if`でも`elseif`でもなく`elsif`なので注意

### 後置if

```rb:back_if.rb
a = 'hoge'
p 'true' if a == 'hoge'
p 'true' unless a == 'moge'
```

個人的に`Ruby`が好きな理由の1つ
条件式を後ろに配置できる。
ものによっては1行でまとまって非常にスッキリするのでオススメ
英語の文法としても自然（と思う）
`PHP`にはないので`PHP`版は割愛

### 演習問題5
1から30の間で`FizzBuzz`を`Ruby`で実装してみましょう。
※ `FizzBuzz`とは3の倍数で`Fizz`, 5の倍数で`Buzz`, 15の倍数で`FizzBuzz`, それ以外の時は数値を出すプログラム問題のことである。

## ハッシュ（連想配列）

```php:hash.php
<?php
$a = array('key1' => 'value1', 'key2' => 'value2');
print_r($a);

foreach($a as $key => $value){
    echo("$key, $value\n");
}

echo($a['key2']."\n");
```

```rb:hash.rb
a = {'key1' => 'value1', 'key2' => 'value2'}
p a

a.each do |key, value|
  puts key + ', ' + value
end

puts a['key2']
```

ハッシュとは簡単に言うと`PHP`でいうところの連想配列である。
キーとそれに対応する値を保持する形
これも連想配列を理解していれば特に難しいことはない。
リテラルが中括弧を使用する点に注意

### 演習問題6
`'key1' => 1`この形で10まであるハッシュをループで生成して下さい。
生成したハッシュに対して、値が5以上であるもののkey値を出力して下さい。
Hint: キャスト

| PHP      | Ruby    | 
|:---------|:--------|
|(int)'5'  |'5'.to_i |
|(string)5 |5.to_s   |


## 関数

```php:function.php
<?php
function add($a, $b){
    $sum = $a + $b;
    return $sum;
}

$result = add(1, 2);
var_dump($result);
```

```rb:function.rb
def add(a, b)
  sum = a + b
  return sum
end

sum = add(1, 2)
puts sum
```

`PHP`の`function`を`def`に置き換えればほぼそのまま流用できる。
ただ`Ruby`では上記のような書き方は普通しない。
通常は以下のような書き方になる。

```rb:function2.rb
def add a, b
  a + b
end

puts add(1, 2)
```

`Ruby`では`return`を省略することができる。
省略した場合は関数内で最後に評価された値が自動で返る（この場合は`a+b`の結果）
ここは`Ruby`のソースを読む上で勘違いしやすいところなので注意。

### 演習問題7
引数: `array`
戻値: `hash`
配列を渡したらその中に入っている数値を全て足した結果を返す関数を作成して下さい。
配列の中が全て整数でなければ加算できないので`success`にfalseを入れて返して下さい。
例:
`[1,2,3,4,5,6,7,8,9,10] >> {'success' => true, 'value' => 55}`
`[1,2,3,'a',5,6,'b',8,9,10] >> {'success' => false, 'value' => nil}`
Hint: nil
`nil`とは`PHP`でいうところの`NULL`と同義である。

| PHP         | Ruby     | 
|:------------|:---------|
|NULL         | nil      |
|is_null(NULL)| nil.nil? |

## 例外処理

```php:exception.php
<?php
try {
    $a = 'moge';
    if($a === 'hoge'){
        echo("hoge\n");
    } else {
        throw new Exception('exception occur');
    }

    echo('finish');
} catch(Exception $e){
    var_dump($e->getMessage());
}
```

```rb:exception.rb
begin
  a = 'moge'
  if a == 'hoge'
    puts a
  else
    raise 'exception occur'
  end
rescue => message
  puts message
end
```

`PHP`の`try-catch`は`Ruby`では`begin-rescue-end`に置き換わる。
また、例外を発生させる方法は`throw`を`raise`に置き換える。

## クラス

```php:class.php
<?php
class ParentClass
{
    public function sayHello(){
        echo("Hello!\n");
    }
}

class ChildClass extends ParentClass
{
}

$a = new ParentClass();
$a->sayHello();

$b = new ChildClass();
$b->sayHello();
```

```rb:class.rb
class ParentClass
  public
  def sayHello
    puts 'Hello'
  end
end

class ChildClass < ParentClass
end

a = ParentClass.new
a.sayHello

b = ChildClass.new
b.sayHello
```

簡単に違いを表す

| PHP       | Ruby     | 
|:----------|:---------|
|extends    | <        |
|new Class  | Class.new|
|$a->hoge() |a.hoge    |

また、`PHP`ではメソッド1つ1つに`public`, `protected`, `private`をつけるのに対し、`Ruby`では宣言箇所以下ではそのアクセス権に絞られる。
以下の例で言うと`a,b,c`が`public`, `d,e`が`protected`, `f`が`private`となる。

```rb
class hoge
public
a
b
c
protected
d
e
private
f
end
```

### 演習問題8
上記の例に`ChildClass`を継承した`GrandChildClass`を追加して下さい。
`GrandChildClass`では`sayHello`メソッドに引数で文字列を渡すとその人に対してHelloするようにメソッドを拡張して下さい。
例: c.sayHello('yamamoto') >> 'Hello! yamamoto!'

## ブロック

ブロックに関しては`PHP`に置き換えるのが難しいため割愛
こちらの記事が大変参考になるので気になる人はこちら

> http://qiita.com/kidach1/items/15cfee9ec66804c3afd2

## おわりに

これで`Ruby`の基本的な書き方は押さえられました。
次回は`Rails`にスポットを当てる予定です。

## Appendix

演習問題の解答例を載せておきます。
解き方は1通りではないし私の解答がイケてない可能性もあるのであくまで例ということで。

### 演習問題3の解答例

```rb:example3.rb
(1..50).each do |i|
  p i * 2
end

# 条件文を使った方法はこっち
#(1..100).each do |i|
#  p i if i % 2 == 0
#end
```

### 演習問題4の解答例

```rb:example4.rb
string = 'Hello, new, world'
array = string.split(', ')
p array

convert_string = array.join(', ');
p convert_string

p convert_string.gsub(',', ':');
```

### 演習問題5の解答例

```rb:example5.rb
(1..30).each do |i|
  print 'Fizz' if i%3 == 0
  print 'Buzz' if i%5 == 0
  print i unless (i%3 == 0 || i%5 == 0)
  puts
end
```

### 演習問題6の解答例

```rb:example6.rb
a = {}
1.upto 10 do |i|
  a['key'+i.to_s] = i
end
p a

a.each do |key, value|
  p key if value >= 5
end
```

### 演習問題7の解答例

```rb:example7.rb
def add_all target_array
  # 全て数値かチェック
  success = number_check(target_array)
  result_hash = {'success' => success}
  if success
    result_hash['value'] = target_array.inject { |sum, n| sum + n }
  else
    result_hash['value'] = nil
  end
  result_hash

end

def number_check target_array
  target_array.each do |i|
    return false unless i.kind_of?(Numeric)
  end
  true
end

target_array = [1,2,3,4,5,6,7,8,9,10]
p add_all(target_array)

target_array = [1,2,3,'a',5,6,'b',8,9,10]
p add_all(target_array)
```

### 演習問題8の解答例

```rb:example8.rb
class ParentClass
  public
  def sayHello
    puts 'Hello'
  end
end

class ChildClass < ParentClass
end

class GrandChildClass < ChildClass
  public
  def sayHello(name)
    puts 'Hello! ' + name + '!'
  end
end

a = ParentClass.new
a.sayHello

b = ChildClass.new
b.sayHello

c = GrandChildClass.new
c.sayHello('yamamoto')
```