第20回 Ruby 勉強会 @ 関西

Posted by okkez Fri, 02 Nov 2007 11:52:00 GMT

行ってきた。

第 20 回 Ruby 勉強会

内容は置いといて、初級者レッスンの解答例(answer.pdf)に補足説明を入れておく。 資料は上記サイトでダウンロードしてくださいな。

以下では、配布資料(print.pdf)が手元にあることを前提に進めていきます。

実装するメソッド

  • empty?
    • スタックが空なら true、そうでなければ false を返す。
  • size
    • スタックのサイズを返す。
  • push(val)
    • 引数の値をスタックの一番上に積む。
  • pop
    • スタックの一番上の値を取り除いて返す。 スタックが空の場合は Stack::EmptyStackErrorが発生する。

テスト駆動開発版

TDD でやってくのです。

Step1

  • Stack#empty? のテスト
  • 「新しいスタックの empty? は真」

ということなのでまずはテストを書きます。

test_stack.rb
require 'stack' require 'test/unit' class TestStack < Test::Unit::TestCase def setup @stack = Stack.new end def test_empty? assert(@stack.empty?, 'a new stack is empty.') end end
空っぽのテストなので簡単ですね。 * Stack#empty? のテストをしたいのでテストメソッド名は test\_empty? * assert は 第一引数の式が真で無い場合にに失敗する で、実装。普通は失敗してから Fake it するのですが、print.pdfでは既に失敗してるのでここではいきなり成功しときます。
stack.rb
class Stack def empty? true end end
ここでは成功さえすれば良いのでいきなり true を返します。 #### Step2 * Stack#push と Stack#pop のテスト * 「新しいスタックに 3 を push して pop すると 3 が返る」 まずテスト。
test_stack.rb
require 'stack' require 'test/unit' class TestStack < Test::Unit::TestCase def setup @stack = Stack.new end def test_empty? assert(@stack.empty?, 'a new stack is empty.') end def test_push_and_pop @stack.push(3) assert_equal(3, @stack.pop, 'pop returns the last value.') end end
* Stack#push と Stack#pop のテストなのでテストメソッド名は test\_push\_and\_pop
stack.rb
class Stack def empty? true end def push(val) end def pop return 3 end end
* Stack#push はテストが通るので、まだ空っぽでいい。 * Stack#pop はテストを通すために 3 を返す。 明らかにテストが不足しているが、まだ書かない。一歩ずつ確実に進んで行く。 #### Step3 * Stack#size のテスト * 「新しいスタックに 3 を push すると size は 1」 なにがなんでもまずテスト。
test_stack.rb
require 'stack' require 'test/unit' class TestStack < Test::Unit::TestCase def setup @stack = Stack.new end def test_empty? assert(@stack.empty?, 'a new stack is empty.') end def test_push_and_pop @stack.push(3) assert_equal(3, @stack.pop, 'pop returns the last value.') end def test_push_and_size @stack.push(3) assert_equal(1, @stack.size, 'push increments the size.') end end
* push してから size を確認するのでテストメソッド名は test\_push\_and\_size * まだ一個しか push しない * 二個 push したらどうなる?とかこの段階では考えない。
stack.rb
class Stack def empty? true end def push(val) end def pop return 3 end def size 1 end end
* 3 を push したら size が 1 になればいいのだから Stack#size は 1 を返せばいい * しつこく Fake it * テストを通すことだけ考える。 #### Step4 * Stack#size のテスト (2) * 「新しいスタックに 3 を push すると size は 1」 * 「さらに 5 を push すると size は 2」 とりあえずテストを書いてみる。
test_stack.rb
require 'stack' require 'test/unit' class TestStack < Test::Unit::TestCase def setup @stack = Stack.new end def test_empty? assert(@stack.empty?, 'a new stack is empty.') end def test_push_and_pop @stack.push(3) assert_equal(3, @stack.pop, 'pop returns the last value.') end def test_push_and_size @stack.push(3) assert_equal(1, @stack.size, 'push increments the size.') @stack.push(5) assert_equal(2, @stack.size, 'push increments the size.') end end
素直に書けばこうなるはず。 実装コード
stack.rb
class Stack def initialize @size = 0 end def empty? true end def push(val) @size += 1 end def pop return 3 end def size return @size end end
* Fake it では通らなくなったので、マジメに実装。 * 放っておいても今のところ大丈夫な部分は放置。 #### Step5 * Stack#empty? のテスト (2) * 「新しいスタックに 3 を push すると empty? は偽」 いつまでも Stack#empty? が true しか返さないのはまずいので、false を返す場合のテストを書いてみる。
test_stack.rb
require 'stack' require 'test/unit' class TestStack < Test::Unit::TestCase def setup @stack = Stack.new end def test_empty? assert(@stack.empty?, 'a new stack is empty.') end def test_push_and_pop @stack.push(3) assert_equal(3, @stack.pop, 'pop returns the last value.') end def test_push_and_size @stack.push(3) assert_equal(1, @stack.size, 'push increments the size.') @stack.push(5) assert_equal(2, @stack.size, 'push increments the size.') end def test_push_and_empty? @stack.push(3) assert_equal(false, @stack.empty?, 'a stack with data is not empty.') end end
で、テストが失敗するのでテストが通るように実装コードを変更します。
stack.rb
class Stack def initialize @size = 0 end def empty? return @size == 0 end def push(val) @size += 1 end def pop return 3 end def size return @size end end
* @size == 0 が true/false のいずれかを返すので return します * 関係ない部分は弄りません #### Step6 * Stack#pop のテスト (2) * 「新しいスタックを pop すると Stack::EmptyStackError が発生する」 誰がなんと言おうがテストコードを先に書きます。
test_stack.rb
require 'stack' require 'test/unit' class TestStack < Test::Unit::TestCase def setup @stack = Stack.new end def test_empty? assert(@stack.empty?, 'a new stack is empty.') end def test_push_and_pop @stack.push(3) assert_equal(3, @stack.pop, 'pop returns the last value.') end def test_push_and_size @stack.push(3) assert_equal(1, @stack.size, 'push increments the size.') @stack.push(5) assert_equal(2, @stack.size, 'push increments the size.') end def test_push_and_empty? @stack.push(3) assert_equal(false, @stack.empty?, 'a stack with data is not empty.') end def test_empty_pop assert_raise(Stack::EmptyStackError, 'to pop a empty stack raise an error.') {@stack.pop} end end
* 空のスタックに対して pop するのでテストメソッドは test\_empty\_pop * assert_raise はブロックに例外が発生する可能性のある処理を記述する 実装コード
stack.rb
class Stack class EmptyStackError < StandardError; end def initialize @size = 0 end def empty? return @size == 0 end def push(val) @size += 1 end def pop raise EmptyStackError if empty? return 3 end def size return @size end end
* Stack#pop を修正。 * Stack#empty? が true の場合に例外を発生させる * 関係ない部分は意地でも書き換えない #### Step7 * Stack#pop のテスト (3) * 「新しいスタックに 3 を push して 5 を push して pop すると size は 1」 例によってテストを先に書きましょう。
test_stack.rb
require 'stack' require 'test/unit' class TestStack < Test::Unit::TestCase def setup @stack = Stack.new end def test_empty? assert(@stack.empty?, 'a new stack is empty.') end def test_push_and_pop @stack.push(3) assert_equal(3, @stack.pop, 'pop returns the last value.') end def test_push_and_size @stack.push(3) assert_equal(1, @stack.size, 'push increments the size.') @stack.push(5) assert_equal(2, @stack.size, 'push increments the size.') end def test_push_and_empty? @stack.push(3) assert_equal(false, @stack.empty?, 'a stack with data is not empty.') end def test_empty_pop assert_raise(Stack::EmptyStackError, 'to pop a empty stack raise an error.') {@stack.pop} end def test_push_push_pop_and_size @stack.push(3) @stack.push(5) @stack.pop assert_equal(1, @stack.size, 'pop decrements the size.') end end
* テストメソッド名は test\_push\_push\_pop\_and\_size * 特別なことはしていません 実装コード
stack.rb
class Stack class EmptyStackError < StandardError; end def initialize @size = 0 end def empty? return @size == 0 end def push(val) @size += 1 end def pop raise EmptyStackError if empty? @size -= 1 return 3 end def size return @size end end
* 関係ない部分は弄らない、と #### Step8 * Stack#pop のテスト (4) * 「新しいスタックに 3 を push して 5 を pushして pop すると 5 が返る」 そろそろ佳境ですが、テストを先に書きます。
test_stack.rb
require 'stack' require 'test/unit' class TestStack < Test::Unit::TestCase def setup @stack = Stack.new end def test_empty? assert(@stack.empty?, 'a new stack is empty.') end def test_push_and_pop @stack.push(3) assert_equal(3, @stack.pop, 'pop returns the last value.') end def test_push_and_size @stack.push(3) assert_equal(1, @stack.size, 'push increments the size.') @stack.push(5) assert_equal(2, @stack.size, 'push increments the size.') end def test_push_and_empty? @stack.push(3) assert_equal(false, @stack.empty?, 'a stack with data is not empty.') end def test_empty_pop assert_raise(Stack::EmptyStackError, 'to pop a empty stack raise an error.') {@stack.pop} end def test_push_push_pop_and_size @stack.push(3) @stack.push(5) @stack.pop assert_equal(1, @stack.size, 'pop decrements the size.') end def test_push_push_and_pop @stack.push(3) @stack.push(5) assert_equal(5, @stack.pop, 'pop returns the last value.') end end
実装コード
stack.rb
class Stack class EmptyStackError < StandardError; end def initialize @size = 0 @values = Array.new end def empty? return @size == 0 end def push(val) @size += 1 @values[@size - 1] = val end def pop raise EmptyStackError if empty? val = @values[@size - 1] @size -= 1 return val end def size return @size end end
外から見た動作ではスタックぽく動くようになりましたね。 さて、 irb で中身を覗いてみましょう。
irb(main):001:0> load 'stack.rb' => true irb(main):002:0> s = Stack.new => # irb(main):003:0> s.push 1 => 1 irb(main):004:0> s.push 2 => 2 irb(main):005:0> s.push 3 => 3 irb(main):006:0> s.inspect => "#" irb(main):007:0> s.pop => 3 irb(main):008:0> s.pop => 2 irb(main):009:0> s.pop => 1 irb(main):010:0> s.inspect => "#" irb(main):011:0>

@values の中身が Stack#pop しても消えませんね。これはスタックとしては拙いのでリファクタリングしましょう。

というわけで次回に続く。

Posted in | コメントはありません | タグ , | atom

Trackbacks

Use the following link to trackback from your own site:
http://typo.okkez.net/trackbacks?article_id=ruby-kansaiworkshop-20th&day=02&month=11&year=2007

Comments

Leave a response

Leave a comment