No Programming, No Life

プログラミング関連の話題や雑記

Groovyで処理とイテレーションの分離

Groovyだと、eachなどのイテレーションメソッドが豊富なので処理とイテレーションの分離が楽にできる。

お題

例えば、


1〜10までの数値が入ったリストから繰り返し数値を取得し
出力するプログラムを完成させよ。ただし、数値が5の時は表示しないこと。

なんてお題があった場合、あなたならどう書くだろうか。
ひとまず、一番簡単に思いつくのは以下のようなコードだろう。

def list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for (i in list) {
   if( i != 5 ) {
      print i
   }
}

問題点

実はこのコードには問題がある。それは何か。

それは、
「数値を繰り返し取得する」というイテレーション(繰り返し)
「5の時は表示しない」という判定処理が同時に行われていることである。
本当はこれらの処理は分離しているほうがプログラムの見通しがよくなる。
つまり、
「リストから5だけ取り去る」という処理と
「リストの内容を全て表示する」というイテレーション
に分離することである。


具体例を次に示そう。

分離されたコード

def list2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
(list2 - 5).each{ print it }

list2 - 5 の部分がリストから5という要素を取り去る部分。
{ print it } の部分が与えられた内容をただ表示する部分である。(クロージャ)


このように分離しておくことで、各々が各々の関心事に集中できるようになる。
これは、例えば「取り去る内容が5〜7に増えました」とか「出力時に3以下は"*"にして下さい」なんていう仕様変更が後から必要になった場合などにも、修正箇所が分離しており、他に影響を与えないため、作業しやすくなる。
また、イテレーションの部分は、クロージャとして処理をまとめられるため、他で再利用を考えることもできるようになる。

まとめ

この考え方をいつも念頭においてプログラムを書くようにすると、複雑な条件でフィルタリングしたり、加工したりして、作り上げたものを、あとは単純に繰り返して全部処理させるというスタイルになってゆく。

def something // 何か未加工のもの
def processed = something.process1().process2().process3()..... // 加工処理を繰り返す
// 加工済みのものを繰り返し
processed.each{
   // 何かの処理
}