2012年3月20日火曜日

[Scala]BddでFizzBuzz[BDD]

BDDという言葉を初めて知りました。

TDDと違って「振る舞い」を意識してテストを書く!! って言われても、あんまり実感がわかない。

実感わかないけど、まあ手を動かしていきます。

Step1.テストを書く

テストのリストを以下に。

import jp.numa.fizzbuzz.FizzBuzz

class FizzBuzzTest extends Spec with ShouldMatchers {
  describe("only Multiples of 3 ") {
    it("should return Fizz ") {
      val multiple3 = List(3, 6, 9, 12, 18, 21, 24, 27)
      val fizz = new FizzBuzz
      for (num <- multiple3) {
        fizz.getFizzBuzz(num) should be("Fizz")
      }
    }
  }

  describe("only Multiples of 5") {
    it("should return Buzz") {
      val multiple5 = List(5, 10, 20, 25, 35, 40, 50)
      val buzz = new FizzBuzz
      for (num <- multiple5) {
        buzz.getFizzBuzz(num) should be("Buzz")
      }
    }
  }

  describe("only Common Multiple of 3 and 5") {
    it("should return FizzBuzz") {
      val cmultiple3x5 = List(15, 30, 45, 60, 75, 90, 105, 120)
      val fizzbuzz = new FizzBuzz
      for (num <- cmultiple3x5) {
        fizzbuzz.getFizzBuzz(num) should be("FizzBuzz")
      }
    }
  }

  describe("only other number") {
    it("should return number to string") {
      val numbers = List(1, 2, 4, 7, 8)
      val other = new FizzBuzz
      for (num <- numbers) {
        other.getFizzBuzz(num) should be(num.toString())
      }
    }
  }
}


FizzBuzz問題と言うことで、

  • Fizzのみ
  • Buzzのみ
  • FizzBuzzのみ
  • 普通の数字のみ

をそれぞれ得るテストを書く。当然、元のコードは書かれていないのでEclipse使っているとコードは真っ赤っ赤。

Step2.コンパイルできるようにする
FizzBuzz#getFizzBuzzはStringを返すメソッドなので、とりあえず hoge を返すメソッドを定義する

package jp.numa.fizzbuzz

class FizzBuzz {
  def getFizzBuzz(number: Int) = "hoge"
}


Step3.失敗を確認する
EclipseでScalaTestを使うのがちょっと面倒くさかった。

  • Run -&gt;RunConfigulation -&gt; Scala Application -&gt; New -&gt;
  • [Main] -&gt; Project : "FizzBuzz" , Main class : "org.scalatest.tools.Runner"
  • [Arguments] -&gt; Program arugments : -s jp.numa08.fizzbuzz.test.FizzBuzzTest

として、Run 一度設定すれば、項目を埋める必用はなくなるけど、Runconfigulation -&gt; Run からでないと、実行できない?

Step4.実装をする

さて、実装。パターンマッチを使ってチョコチョコっと書いていきます。

package jp.numa.fizzbuzz

class FizzBuzz {
  def getFizzBuzz(number: Int) = {
    number match {
      case num if (num % 3 == 0 && num % 5 == 0) => "FizzBuzz"
      case num if (num % 3 == 0) => "Fizz"
      case num if (num % 5 == 0) => "Buzz"
      case num => num.toString()
    }
  }
}


Steo5.通るまでテスト
実は、テストのコードをミスっていてテストが通らないで困っていたりもしましたけど・・・

Steo6.動くものを作る
Mainオブジェクトを作りました。

package jp.numa.fizzbuzz

object Main {

  def main(args: Array[String]): Unit = {
    val fizzbuzz = new FizzBuzz
    for (num <- 1 to 100) {
      print(fizzbuzz.getFizzBuzz(num) + "\n")
    }
  }

}


さて、完成ですかね。

格好つけて再帰とか使いたいなぁみたいなことはちょっと考えていたら、@kmizuに”再帰は最後の手段”って言われたので見送り(決して方法を思いつかなかったわけでは無い。)

まあ、間違えることは無いだろうけど確認のために anarch golf Fizz Buzz で確認をしておきました。と言っても、フォームからの投稿がなんか上手くいかないので、実行結果をコピペして、diffコマンドで比較したわけですが。

しばらく、Scalaで頑張ります。

2012年3月18日日曜日

[シェルスクリプト]デバッグを使ってコーデング[cygwin]





昨日の続きで、シェルスクリプトのデバッグ機能を利用してコードを書く。

今回やろうとしているのは、dfコマンドで取得したハードディスクの使用率が80%を超えている物を出力しようと言う物。

最初に書いたコードは以下

 #!/bin/sh

df | while read target; do
         usage=`echo $target | awk '$1 ~ /:$/ {print $5;}' | sed s/%//g`
         if [ $usage -ge 70 ]; then
                 volume=`echo $target | awk '{print $1;}'`
                 echo $volume is used $usage %...
         fi
done


4行目のawkで、Windowsのパーティションの情報のみを正規表現で得ている、つもり、なのだがうまく動いていない。

ちなみに、dfコマンドの出力結果は


\ファイルシス   1K-ブロック      使用    使用可 使用% マウント位置
E:/cygwin/bin     74035520  33040348  40995172   45% /usr/bin
E:/cygwin/lib     74035520  33040348  40995172   45% /usr/lib
E:/cygwin         74035520  33040348  40995172   45% /
C:               167132192 117595092  49537100   71% /cygdrive/c
D:               732555928 386095748 346460180   53% /cygdrive/d
E:                74035520  33040348  40995172   45% /cygdrive/e
F:                51705168    292732  51412436    1% /cygdrive/f
G:               472576040  76612632 395963408   17% /cygdrive/g
H:               594027440 184795676 409231764   32% /cygdrive/h
I:               594035472  93392484 500642988   16% /cygdrive/i
J:               732563992 414357640 318206352   57% /cygdrive/j
K:               488391676 368850304 119541372   76% /cygdrive/k
L:                 6918814   6918814         0  100% /cygdrive/l
Y:               356670332 102977136 253693196   29% /cygdrive/y


こんな感じ。1フィールド目が : で終わるもののみを取得している、つもり。うーん・・・

こういうことも

sh -x

で分かる。上記のコードを実行すると


+ df
+ read target
++ echo $'\203t\203@\203C\203\213\203V\203X' $'1K-\203u\203\215\203b\203N' $'\216g\227p' $'\216g\227p\211\302' $'\216g\227p%' 'マウント位置'
++ awk '$1 ~ /:$/ {print $5;}'
++ sed s/%//g
+ usage=
+ '[' -ge 80 ']'
hdd_checker.sh: line 5: [: -ge: unary operator expected
+ read target
++ echo E:/cygwin/bin 74035520 33040348 40995172 45% /usr/bin
++ awk '$1 ~ /:$/ {print $5;}'
++ sed s/%//g
+ usage=
+ '[' -ge 80 ']'
hdd_checker.sh: line 5: [: -ge: unary operator expected
+ read target
++ echo E:/cygwin/lib 74035520 33040348 40995172 45% /usr/lib
++ awk '$1 ~ /:$/ {print $5;}'
++ sed s/%//g
+ usage=
+ '[' -ge 80 ']'
hdd_checker.sh: line 5: [: -ge: unary operator expected
+ read target
++ echo E:/cygwin 74035520 33040348 40995172 45% /
++ awk '$1 ~ /:$/ {print $5;}'
++ sed s/%//g
+ usage=
+ '[' -ge 80 ']'
hdd_checker.sh: line 5: [: -ge: unary operator expected
+ read target
++ echo C: 167132192 117588328 49543864 71% /cygdrive/c
++ awk '$1 ~ /:$/ {print $5;}'
++ sed s/%//g
+ usage=71


変数$usageの中に適切な値が入っていないので、if文で怒られているわけだ。と、まあここまで書いて気づいたけど、awkは確かに処理をしていない。

というか、処理をしていないからこそ、usage変数に何も入らなくなってifで怒られている。

どうすれば良いのか・・・

ちなみに今回の場合、目的を達成するだけであればawkのみを利用して

#!/bin/sh

df | awk '$1 ~ /:$/ {sub(/%/,"",$5); if($5+0 >= 70) print $1 " is used " $5"%"}'


そんな、目的と手段を見失った日曜の午前中・・・。

シェルスクリプトでデバッグ

USP友の会 なる組織があることを初めて知りました。

以前書いた、VMwareEsxiのバックアップスクリプトの動きがおかしかった。

コメントアウトをしたり、色々と調べた結果、スナップショットの作成時点でコケていた模様。

#vim-cmd vmsvc/getallvms

で、全ての仮想マシンの名前やIDを得ることができる。スナップショットを作るための

#vim-cmd vmsvc/snapshot.create

は引数に仮想マシンのIDをとるので、awkを使って

vim-cmd vmsvc/getallvms | awk '/$1/ {print $1;}'

で、ID部分のみを取得できる、はずだった。

しかし、動かない。awkの文法で何かをミスっているらしい。

結論から言うと

vim-cmd vmsvc/getallvms | awk '/'$1'/ {print $1;}'

が正解。標準入力からの引数を文字として認識してしまっていたらしい。

で、調査方法だけどUSP友の会の会長さん(@usptomo)さんが拾ってくれた

sh -vx

こんなのあったんだな。


っていうか シェル デバッグ で検索すると沢山引っかかる・・・検索方法が悪かったですorz

2012年3月12日月曜日

ループ内でのインスタンス作成

ちょっと、釈然としない問題があったのでメモ。

Befor

for (final String imageUrl : stringlList) {
    final URL url = new URL(imageUrl);//Warning
    ...
 }


このコードだと、PMDで ループ内でインスタンスを作るのは避けてください(Avoid instantiating new objects inside loops)が表示されてしまう。 色々と解決方法を検討した結果 After

for (final String imageUrl : stringList) {
    final URL url = createURL(stringList);
   }

private URL createURL(final String url) throws MalformedURLException {
 return new URL(url);
}


とすることで警告を消すことができた。 メソッドに分けただけで、同じじゃないか・・・? 可読性とかも変わっていないような気がするし、メモリ管理の面で見ても結局はループ内でのインスタンスを作成していることに変わりは無い。 まあ、そのうちベンチマークしてみるとするか・・・。

2012年3月2日金曜日

[JUnit]DatePickerをテストケースから操作する[Android]

Androidのテストでは、ウィジェットの操作はUIのスレッドで動作させる必用があるとのこと。

今回は、DatePickerを操作する。ソースは以下。

  activity = getActivity();

  fromDate = (DatePicker) activity.findViewById(R.id.from_data);
  toDate = (DatePicker) activity.findViewById(R.id.to_data);
  activity.runOnUiThread(new Runnable() {

   @Override
   public void run() {
    // TODO Auto-generated method stub
    fromDate.init(fromY, fromM, fromD, null);
    toDate.init(toY, toM, toD, null);
   }
  });
アクティビティのインスタンスから、runOnUiThreadを使ってUIのスレッドでの動作を行わせるとのこと。

今夜はリファクタリング的なことをやって、無駄に増えていった処理用のクラスを纏めていました。


リファクタリングとは、外部から見た時の挙動が変わってはいけない、とのこと。この本に書かれていた。

多分、クラスを分ける必用があるんだろうなぁとは思うけれど、どう分ければいいのかがよく分かっていなくて、後から見返すとかなりアレなことになっていることが多い。