CodingDOJO

Clean Coder でググッてたら CodingDOJO なるソレがあるらしいことを知る。プロのプログラマになるための必読書「Clean Coder」というエントリによれば

ボブおじさんはコーディング道場(Coding Dojo)という名前でボウリングゲームのTDDを何回も、何年も実演しているらしい。これで、プログラミングがめちゃくちゃ上達したらしい。

空手でいうところの型らしい。ウォームアップというか、基本というか。そんな感じ。

プロのプログラマになるための必読書「Clean Coder」より引用
との記述がある。型ッスか。てことで CodingDOJO が以下。

KataBowling

見つつ Gauche で試験と実装を書いてみた。サイズ的に小さめで簡単に反復練習できそう。てか実装はリファクタリングしたいですがそのまま出します。

最初の一歩

パーフェクトから書いた。試験が以下なカンジ。

(use gauche.test)
(add-load-path ".")
(load "kataBowling")

(test-start "kataBowling")
(test-section "kataBowling")

(test* "(bowling '(10 10 10 10 10 10 10 10 10 10 10 10))"
       300
       (bowling '(10 10 10 10 10 10 10 10 10 10 10 10)))

(test-end)

で、最初の実装が以下。

(define bowling
  (lambda (l)
    (let f ((l l) (frame 0) (p 0))
      (if (or (null? l) (>= frame 10))
	  p
	  (cond ((= (car l) 10) 
		 (f (cdr l) (+ 1 frame) (+ p (car l) (cadr l) (caddr l))))
		 )))
    ))

これを元にストライクとスペアが混在しているヤツとか

(test* "(bowling '(10 5 5 10 5 5 10 5 5 10 5 5 10 5 5 5))"
       (+ 20 20 20 20 20 20 20 20 20 15)
       (bowling '(10 5 5 10 5 5 10 5 5 10 5 5 10 5 5 5)))

5 点づつ取ってくスペアばっかのヤツとか

(test* "(bowling '(5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5))"
       (+ 15 15 15 15 15 15 15 15 15 15)
       (bowling '(5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5)))

ストライクもスペアもないやつとか

(test* "(bowling '(2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))"
       5
       (bowling '(2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)))

(test* "(bowling '(1 2 3 4 5 4 3 2 1 2 3 4 5 4 3 2 1 2 3 4))"
       (+ 3 7 9 5 3 7 9 5 3 7)
       (bowling '(1 2 3 4 5 4 3 2 1 2 3 4 5 4 3 2 1 2 3 4)))

(test* "(bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 5))"
       15
       (bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 5)))

あるいは全てが混在しているヤツとか

(test* "(bowling '(10 2 3 5 5 2 3 10 5 5 2 3 5 5 10 2 3))"
       (+ 15 5 12 5 20 12 5 20 15 5)
       (bowling '(10 2 3 5 5 2 3 10 5 5 2 3 5 5 10 2 3)))

実装てきにはスペアな分岐を書いて else を書いたらできちゃったカンジ。

(define bowling
  (lambda (l)
    (let f ((l l) (frame 0) (p 0))
      (if (or (null? l) (>= frame 10))
	  p
	  (cond ((= (car l) 10) 
		 (f (cdr l) (+ 1 frame) (+ p (car l) (cadr l) (caddr l))))
		((= (+ (car l) (cadr l)) 10)
		 (f (cddr l) (+ 1 frame) (+ p 10 (caddr l))))
		(else
		 (f (cddr l) (+ 1 frame) (+ p (car l) (cadr l))))
		)))
    ))

時間的にもそんなにかかってません。Scheme が優秀なのかな。
でも試験にパスしてるとは言え、なんつーか微妙な違和感がある。何だろ。

てか

TDD とか XP とかって割にボッチソンになっちゃってる件orz
そりゃ良いのですが、1h 程度のセッションでヤッてみるのは良いかも、って思っております。Okinawa.rb でもやってみては如何かと。

CodingDOJO 追補

以下な手順で進める模様。

  • プロジェクタに接続された1台のPCでコーディングする。
  • ペアでコーディングする。
  • 5〜10分間隔でペアの片方を交代する(筆者の経験では7分単位での交代がうまくいった)。
  • コーディングを担当しているときは、自分が何をしているのかを説明しながらキーボードをタイプする。こうすることで聴衆も、何が起きているのかを理解できる。
  • 聴衆は、テストがきちんと通っている場合にだけ、設計について意見を述べてもよい。テストが通っていない状態では、設計については質問しかできない。
  • 聴衆が現在おこなわれている作業について混乱してきたら、コーディングしている人は手を止めて、自分がいまやっていることを説明しなければならない。

TDDを根づかせる:導入の問題と解決策より引用

さらに追記

なんとなく違和感があったのでもう少し確認。例えば以下。

(test* "(bowling '(0 0 0 0 0 0 0 0 10 5 5 5))"
       (+ 20 15)
       (bowling '(0 0 0 0 0 0 0 0 10 5 5 5)))

ほら駄目。

test (bowling '(0 0 0 0 0 0 0 0 10 5 5 5)), expects 35 ==> ERROR: GOT #<<error> "pair required, but got ()">

あ、これは試験の方が駄目じゃん。以下か。

(test* "(bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 5 5 5))"
       (+ 20 15)
       (bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 5 5 5)))

で、実装を以下に修正してます。

(define bowling
  (lambda (l)
    (let f ((l l) (frame 0) (p 0))
      (cond ((or (null? l) (>= frame 10)) p)
	    ((= frame 9)
	     (f (cdr l) frame (+ p (car l))))
	    ((= (car l) 10)
	     (f (cdr l) (+ 1 frame) (+ p (car l) (cadr l) (caddr l))))
	    ((= (+ (car l) (cadr l)) 10)
	     (f (cddr l) (+ 1 frame) (+ p 10 (caddr l))))
	    (else
	     (f (cddr l) (+ 1 frame) (+ p (car l) (cadr l))))
	    ))
    ))

一応試験パス。もう少し色々確認。以下とか。

(test* "(bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 2 5))"
       (+ 17 7)
       (bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 2 5)))

(test* "(bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 2 5))"
       (+ 12 7)
       (bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 2 5)))

(test* "(bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3 2 5))"
       (+ 5 7)
       (bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3 2 5)))

パンチアウトなパターンも確認してみるか。

(test* "(bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 10 10 10))"
       (+ 30 30)
       (bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 10 10 10 10)))

(test* "(bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 10 10 10))"
       (+ 20 30)
       (bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 5 5 10 10 10)))

(test* "(bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3 10 10 10))"
       (+ 5 30)
       (bowling '(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 3 10 10 10)))

よし。散歩に行ってこよ。

の前にもう少し

前の実装で動作確認してみたんですが試験パスしとるな。とりあえずリファクタ版、ってことにしておこうと思いますが、やっぱなんでちゃんと動いとるかが微妙に謎。

確認してみたのですが

最初の実装で十分だった模様です。いやはや。