【Ruby】 抵抗器を表現するgemをつくった

## 概要
抵抗器のカラーコードを抵抗値に変換したり、合成抵抗を計算したりするgemをつくりました。名前は `resistor` です。レジスタと聞いて最初に思い浮かぶ方のレジスタ(register)ではなく抵抗器を意味するレジスタ(resistor)です。

### インストール
    $ gem install resistor

## 追記(更新内容)  
gemの更新内容を追記していきます。
それに伴い記事の内容も編集するので予めご了承ください。
### 2015/02/11 v1.0.0
* Resistor.newする際にohmやcodeを指定しないよう変更
* `error_range` としていた部分を全て `tolerance` に変更
* 合成抵抗を計算するメソッドにそれぞれエイリアスを追加（#+に#-, #/に#|）

### 2015/02/15 v1.1.0
* 5本帯に対応
* 5本帯用にE48,E96系列判定メソッドを追加

## 抵抗のカラーコード
抵抗のカラーコードは4本帯の場合、例えば **「茶赤茶金」** となっていたら  
12*10^1 = **120 [Ω]**  
になります。1、2本目は有効数字、3本目は乗数、4本目の色は許容差で、その辺にある4本帯では金(±5%)であることが多いです。  

それぞれの色と数値の対応は以下のようになっています。


| 色 | 数値 | 乗数 | 許容差 |
|:---|:----:|:-----:|:--------:|
| 黒 (black) | 0 | 10^0  |  |
| 茶 (brown) | 1 | 10^1  | ±1% |
| 赤 (red) | 2 | 10^2  | ±2% |
| 橙 (orange) | 3 | 10^3  | ±0.05% |
| 黄 (yellow) | 4 | 10^4  |  |
| 緑 (green) | 5 | 10^5  | ±0.5% |
| 青 (blue) | 6 | 10^6  | ±0.25% |
| 紫 (purple) | 7 | 10^7  | ±0.1% |
| 灰 (gray) | 8 | 10^8  |  |
| 白 (white) | 9 | 10^9  |  |
| 銀 (silver) |   | 10^-2 | ±10% |
| 金 (gold) |   | 10^-1 | ±5% |

ひとつ目の機能としてこのカラーコード変換の計算をやってくれます。  

```ruby
require 'resistor'

# 抵抗値 -> カラーコード
Resistor::ColorCode.encode(4700)
# => [:yellow, :purple, :red, :gold]

# カラーコード -> 抵抗値
Resistor::ColorCode.decode([:yellow, :purple, :red, :gold])
# => 4700.0
```

### Resistor::ColorCode.encode
* 抵抗値[Ω]を渡すとカラーコードの配列(要素がシンボルの配列)が返ってくる
* `Resistor::ColorCode.encode(100, tolerance: 1)` のようにすると許容差を指定できる
* 0を渡すと `[:black]` が返ってくる
* 0.1Ω以上99MΩ以下までの数値で正しい結果が返ってくる

### Resistor::ColorCode.decode
* カラーコードの配列(要素がシンボルか文字列の配列)を渡すと抵抗値[Ω]が返ってくる
* `[:black]` を渡すと0.0が返ってくる
* 配列以外のものを渡されるとつらい
* 先ほどの表以外の色が要素に含まれているとつらい

いい名前が思い浮かばなかったので抵抗値をカラーコードに変換することをエンコード、その逆をデコードとしました。  

0Ωの抵抗だけは黒黒黒とかでなく黒一本なのでここでもそれに従いました。

## 合成抵抗の計算

```ruby
require 'resistor'

Resistor.new(20)
# => #<Resistor::BasicResistor:0x00000000f205a8 @ohm=20.0, @code=[:red, :black, :black, :gold], @tolerance=5.0>
Resistor.new(['red', 'red', 'red', 'gold'])
# => #<Resistor::BasicResistor:0x00000000f03f48 @ohm=2200.0, @code=[:red, :red, :red, :gold], @tolerance=5.0>
```

上記のように抵抗値(Integer, Float)かカラーコード(Array)のどちらかを渡すことで、抵抗値( `@ohm` )、カラーコード( `@code` )、許容差( `@tolerance` )をインスタンス変数にもったオブジェクトをつくることができます。  

このクラスのオブジェクトには合成抵抗を計算するメソッドがあり、 **直列接続** のときは `+` 、 **並列接続** のときは `/` を使います。

```ruby
require 'resistor'

r1 = Resistor.new(20)
r2 = Resistor.new(30)
r3 = Resistor.new(4)
r4 = Resistor.new(8)
```

### 直列の場合

```ruby
# --[r1]--[r2]--[r3]--

r5 = r1 + r2 + r3
p r5.ohm
# => 54.0
```

### 並列の場合

```ruby
#     --[r2]--
# -- |--[r2]--|--
#     --[r2]--

r5 = r2 / r2 / r2
p r5.ohm
# => 10.0
```

### 直列と並列の組合せ

```ruby
#    --[r1]--          --[r4]--
# --|        |--[r3]--|        |--
#    --[r2]--          --[r4]--

r5 = (r1 / r2) + r3 + (r4 / r4)
p r5.ohm
# => 20.0
```

余談ですが合成抵抗を計算するメソッドの戻り値は `Resistor.new` してできたオブジェクトとは異なり `@ohm` しかインスタンス変数がありません。  

合成抵抗を計算して出てきた値が必ずしもきれいな値になるとは限らず、その値をそのまま保持しておきたかったからです。合成抵抗を計算するメソッドはどちらのクラスのオブジェクトにもあるので、上記のように計算ができます。

## 5本帯抵抗
v1.1.0から5本帯抵抗に対応しました。4本帯がデフォルトになっていますが、いくつかの方法で5本帯に切り替えることができます。

オプションで指定するとその場限りで5本帯用の処理をしますが、毎回オプションで指定しなくてもいいように、デフォルト値自体を変更するメソッドもあります。

* `Resistor.new(2.21, band_number: 5)` のようにオプションで指定します。
* `Resistor::ColorCode.encode(100, band_number: 5)` decodeも同様に指定できます。
* `Resistor::Options.set_band_number` 以降のデフォルト値を変更できます。引数は4か5のみで、それ以外の数字を渡しても しかし なにも おこらなかった!

`set_band_number` を使うとオプションで指定する際と異なり、許容差のデフォルト値も同時に変更されます。また、4本帯では許容差5%、5本帯では許容差1%をセットにしていますが、強引にオリジナルのデフォルト値のセットを指定することもできます。

```rb
require 'resistor'

# 以降全てのデフォルトが5本帯で許容差1%に変更される
Resistor::Options.set_band_number 5

Resistor.new(2.21)
# => #<Resistor::BasicResistor:0x00000002933b48 @ohm=2.21, @code=[:red, :red, :brown, :silver, :brown], @tolerance=1.0>
Resistor::ColorCode.encode(100)
# => [:brown, :black, :black, :black, :brown]


# 以降全てのデフォルトが5本帯で許容差0.5%に変更される
Resistor::Options.defaults do |d|
  d[:band_number] = 5
  d[:tolerance] = 0.5
end

Resistor.new(2.21)
# => #<Resistor::BasicResistor:0x00000001717ae0 @ohm=2.21, @code=[:red, :red, :brown, :silver, :green], @tolerance=0.5>
Resistor::ColorCode.encode(100)
# => [:brown, :black, :black, :black, :green]

```

## E系列の判定
抵抗器やコンデンサの値は、E系列という規格化された数列表に基づいた値になっていて、許容差5%の抵抗の場合はE24系列というものが使われています。  

そこで `Resistor.new` でつくられたオブジェクトに対してどの系列であるかを調べるメソッドをつくりました。  

本来、E系列は許容差と関係があるのですが、ここでは単純にカラーコードの数字だけをみて真偽を判定します。

このgemではE系列の内、

* `#e12?` E12系列であるなら `true` ,それ以外は `false`
* `#e24?` E24系列であるなら `true` ,それ以外は `false`
* `#e48?` E48系列であるなら `true` ,それ以外は `false` (5本帯専用)
* `#e96?` E96系列であるなら `true` ,それ以外は `false` (5本帯専用)

の４つが使えます。 `#e48?` , `#e96?` に関しては4本帯の抵抗である場合、常に偽となります。

```ruby
require 'resistor'

r1 = Resistor.new(4700)
r2 = Resistor.new(62)

r1.e12? # => true
r1.e24? # => true
r2.e12? # => false
r2.e24? # => true

Resistor::Options.set_band_number 5

r3 = Resistor.new(105)
r4 = Resistor.new(4.99)

r3.e48? # => true
r3.e96? # => true
r4.e48? # => false
r4.e96? # => true
```

## おわりに
今回初めてgemを自作してみたのですが、とてもいい勉強になりました。  

ソースコードは [ここ](https://github.com/seinosuke/resistor) にあったりなかったりします。 

ご意見、ご感想、ごマサカリ等お待ちしております。