10の練習問題をやってみよう(六日目)

新しくプログラミング言語を覚えたいときに行うべき10の練習問題
実際にやってみよう。

第四問

簡易電卓。0で割ったときの処理を自分なりに作りこむ。


いきなり感想

ホンモノの電卓をパチパチと叩いて動作を確認しながらやりました。
それで気がついたのですが、電卓を起動すると最初から謎の少数点が表示されているんですね。

0.

「これはきっと常に右下に表示されているんだろう」と思ったのですが、「0.5」とか入力すると、ちゃんと左に移動しているようです。まさかと思ってWindowsについてくる電卓でも確認したら、やっぱり同じように謎の少数点がついてました。この小数点の意味は何なんだろう?電卓業界では常識なのかな?今作っている電卓クラスで謎の少数点を実装しておいたら、カシオ計算機の電卓マンが見たときに「ほほぅ、コイツ分かってるな」って一目置かれたりするのかな?イヤでも電卓の専門家がプログラム言語の練習問題なんか見ないよなー。全然関係ないけど、今作っているのは電卓クラスだから、「ボタンを押す」っていう関数を呼んで数字を入力しているわけだけど、これがそろばんクラスだったら「珠を弾く」とか「五の珠をジャーってやる」とかを実装するのかな。
そんなことばかりモンモンと考えていたら夜が更けていきました。

小数点ボタン

前回で数字ボタンはできた。次は小数点ボタンを押せるようにしよう。
小数点はややこしい。2回連続で少数点ボタン押されたら無視しないといけないし、起動した直後に「.」「5」って押したら「0.5」になんないといけないし。画面に表示させる数字は文字列で持っとけばいいのか数値で持っとけばいいのかで悩んだりして、結局配列プラス整数部の桁数を持つことにしました。

#
# 電卓クラス
#
class Calc
  
  # コンストラクタ
  def initialize
    @keta = nil # 整数部の桁数
    @nums = [0] # 画面に表示される数値の配列
  end
  
  # ボタンを押す
  def push( button )
    # 押されたボタンの種類によってメソッドを分けよう
    case button.to_s
    when '0' .. '9'
      push_num button
    when '.'
      push_dot
    end
    # 戻り値はディスプレイに表示される文字列
    display
  end
  
  # 数字ボタンを押す
  def push_num( num )
    # 画面が初期状態だったら先頭の「0」を消す
    if @nums == [0] && @keta == nil
      @nums.pop
    end
    # 押されたボタンの数字を末尾に追加
    @nums << num
  end
  
  # 小数点ボタンを押す
  def push_dot
    # 整数部の桁数を覚えておく
    if @keta == nil
      @keta = @nums.size
    end
  end
  
  # ディスプレイに表示される文字列
  def display
    # コピー
    dup = @nums.dup
    # 数字の配列に少数点を差し込む
    if @keta != nil && @keta != @nums.size
      dup[@keta, 0] = '.'
    end
    # 連結して返す
    dup.join
  end
  
end

なんだかごちゃごちゃしてきたなぁ・・・if文が多いなぁ・・・最後まで実装できるのかなぁ・・・不安になってきた。

テストケース

$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..' , 'lib')

require 'test/unit'
require 'calc'

# 電卓クラスのテストケース
class TestCalc < Test::Unit::TestCase
  
  # 初期化メソッド。各テストメソッドの前に実行される。
  def setup
    @calc = Calc.new
  end
  
  # 数字ボタンのテスト [0] [1] [0] の順に押す
  def test_push_num
    assert_push( '0', 0);
    assert_push( '1', 1);
    assert_push('10', 0);
  end
  
  # 小数点ボタンのテスト [1] [.] [5] の順に押す
  def test_push_dot
    assert_push('1'  ,   1);
    assert_push('1'  , '.');
    assert_push('1.5',   5);
  end
  
  # 小数点ボタンのテスト [1] [.] [.] [5] [.] [0] の順に押す
  def test_push_dot_double_reppuuken
    assert_push('1'   ,   1);
    assert_push('1'   , '.');
    assert_push('1'   , '.');
    assert_push('1.5' ,   5);
    assert_push('1.5' , '.');
    assert_push('1.50', '0');
  end
  
  # 小数点ボタンのテスト [.] [2] [0] の順に押す
  def test_push_dot_first
    assert_push('0'   , '.');
    assert_push('0.2' ,   2);
    assert_push('0.20',   0);
  end
  
  # 手抜き用関数
  # 「buttonを押したらexpectが表示されるハズ」っていう気持ち
  def assert_push( expect, button )
    assert_equal( expect, @calc.push( button ) )
    assert_equal( expect, @calc.display)
  end
  
end

テストケースがあると、苦労して作ったソースコードを書き換えるのをためらわなくなります。話には聞いてたけど、実際にやってみるとよくわかりますな。なんとも心強いです。