第7回 :: ruby_third

\![Mac OS X-10.15.7](<https://img.shields.io/badge/MacOSX-10.15.7-brightgreen>) \![ruby-2.7.1p83$](<https://img.shields.io/badge/ruby-2.c7.1p83-brightgreen>)


# if-elsif-else-end


## お題

入力がうるう年(leap year)かどうかを表示するプログラムをかけ．

1.  うるう年は4で割り切れる数の年．ただし，
2.  100で割り切れる年はうるう年でなく，
3.  400で割り切れる年はうるう年．
4.  2004, 1999, 1900, 2000で試せ．
5.  それぞれtrue, false, false, true


## rubyのif

[オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル](https://docs.ruby-lang.org/ja/index.html)

上記のサイトには以下のように書かれている

```ruby
if 式 [then]
  式 ...
[elsif 式 [then]
  式 ... ]
...
[else
  式 ... ]
end
```

上の表現で[]は省略可能箇所を示している．従って，最小で

```ruby
if year%4==0 then
  p 'not leap year'
end
```

とかで書ける．さらにthenは省略可能で，ifをつなげるのはelsif．ややこしい．


## 類題:2004年はleap year

    > ruby check_leap_2004.rb 2004

とした時に

    true

と出力するプログラムを作る

```ruby
p year = ARGV[0].to_i
if year % 4 == 0
  p true
end
```

上記のコードを実行すると

    > ruby check_leap_2004.rb 2004
    2004
    true

となる．


## 類題:1999はfalse

```ruby
p year = ARGV[0].to_i
if year%4 == 0
  p true
else
  p false
end
```

上記のコードを実行すると

    > ruby check_leap_0.rb 1999
    1999
    false

ちゃんと出力されてる．


## 類題:1900年と2000年は?

```ruby
p year = ARGV[0].to_i
if yeay % 400 == 0
  p true
elsif year % 100 == 0
  p false
elsif year % 4 == 0
  p true
else
  p false
end
```

条件増やすだけ．


# array, each


## テストの自動化

この後，さらにテストの内容が増えてくると，いちいち引数(ARGV[0])で全てチェックするのが面倒．そこで，テストを自動化するために配列にテストする年を入れて，loopで回す．

```ruby
[2004,1999,1900,2000].each do |year|
  p year
  if yeay % 400 == 0
    p true
  elsif year % 100 == 0
    p false
  elsif year % 4 == 0
    p true
  else
    p false
  end
end
```


## 類題:refactoring\_methodに

さっき書いたコードをメソッド化する

```ruby
def leap?(year)
  if year % 400 ==0
    p true
  elsif year % 100 ==0
    p false
  elsif year % 4 == 0
    p true
  else
    p false
  end 
end
[2004,1999,1900,2000].each do |year|
  p year
  leap?(year)
end
```


# case


## お題:case

さっきのコードをcaseを使って短くする


## caseの文法

先ほどの[オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル](https://docs.ruby-lang.org/ja/2.5.0/doc/spec=2fcontrol.html#if)にcaseの解説がある以下は文法

```ruby
case [式]
[when 式 [, 式] ...[, `*' 式] [then]
  式..]..
[when `*' 式 [then]
  式..]..
[else
  式..]
end
```


## 解法

```ruby
def leap?(year)
  return case
    when year % 400 ==0
      true
    when year % 100 ==0
      false
    when year % 4 ==0
      true
    else
      false
  end
end
[2004,1999,1900,2000].each do |year|
  p year
  leap?(year)
end
```

上記のコードを実行すると

    > ruby leap_year.rb
    2004
    true
    1999
    false
    1900
    false
    2000
    true

もっと短いのあった…

```ruby
def leap?(year)
  case
  when year % 400 ==0 ; return true
  when year % 100 ==0 ; return false
  when year % 4 ==0 ;   return true
  else ;                 return false
  end
end
```


# もっとすごいtdd

上の知識があると全く逆からのtddも可能．

```ruby
def leap?(year)
  case 
  when year == 2000 ; p true
  when year == 1900 ; p false
  when year == 2004 ; p true
  else              ; p false
  end
end
```

より一般化して，

    2000 -> % 400 == 0

とかにしても問題ない．

さらに，

```ruby
def leap?(year)
  return case
	 when year % 400 ==0 ; true
	 when year % 100 ==0 ; false
	 when year % 4 ==0   ; true
	 else                ; false
	 end
end
```

とかもできる．


# おまけ(三項演算子: ternary operator)

if\_elseをもう少しスマートに書く方法があるらしい．

```ruby
year = 1999
if year %4 ==0 then
  return true
else
  return false
end

return year%4 == 0 ? true : false
```

    operation condition ? true_case : false_case

という文法でif-else-endを短く書くことが可能．

もう一つ，

```ruby
return true if year %4 ==0
```

というようにelseがない場合は，ifを後ろに持っていくことが可能で，これも短くなる．unlessでも使えるらしい．実際に動かしてはない．

```ruby
def leap?(year)
  return  (year%400 == 0) ? true :
    (year%100 == 0) ? false :
    (year%4 == 0) ? true : false
end

[2004, 1999, 1900, 2000].each do |year|
  p year
  p leap?(year)
end
```

と短くかけるんやって．



------
 - **source** ~/grad_members_20f/members/okamoto0910/article/c7.org