データとロジックを分割せよ

  • 15
    いいね
  • 2
    コメント

僕が普段プログラムを書くにあたって気をつけるようにしている基本的なことを文章化してみます。

本当に単純なことなので、プログラミング初心者からでも学べる内容になっています。

FizzBuzzが書けるぐらいからを想定しています。

データとロジックを分割せよ

私はこれまでプログラマーとして働いてきた経験は4~5年程度です。
これまでの仕事の経験や、いくつかの書籍を読んだ経験から、プログラミングをする際に気をつけるべき一つのルールに気が付きました。

プログラミングは「データ」と「ロジック」の2つがあることを意識すると、
考えがグッと整理されてイケてるプログラムになりやすいのです。

FizzBuzz

実際にコードを書いてみましょう。

お題を出します。

「FizzBuzzを書いてください。」

ルールを知らない方はこちらへ。 https://ja.wikipedia.org/wiki/Fizz_Buzz

...(しばらく待ちます。実際に書いてみてください。)...






答え合わせをしてみましょう。
例はRubyにしていますが、「データとロジックを分割する」考えは言語を超えた共通の基礎だと思っています。
Rubyを普段使わない方のためにコメントを付けて捕捉しています。

(1..30).each do |i| # 1〜30の数字でループする
  if i % 15 == 0    # 15で割ったあまりが0なら
    puts "FizzBuzz" # "FizzBuzz"を標準出力に表示させる
  elsif i % 5 == 0  # 5で割ったあまりが0なら
    puts "Buzz"
  elsif i % 3 == 0  # 3で割ったあまりが0なら
    puts "Fizz"
  else              # どれでもないなら
    puts i          # 数字そのものを表示させる
  end
end

だいたいこんな感じのコードを書いた方が多いのではないでしょうか。

はっきり言って、このコードは全然ダメです。

このプログラムでは、数字の範囲を勝手に1〜30と決めてしまっています。
これが、あとから「100〜130で表示させてみて」と言われたらどうでしょう。
一度、プログラムの一部を修正して再度実行することになります。

@@ -1,4 +1,4 @@
-(1..30).each do |i|
+(100..130).each do |i|
   if i % 15 == 0
     puts "FizzBuzz"
   elsif i % 5 == 0

では「200〜230のときは?」「300〜330のときは?」と言われるたびにコードを書き換えるのでしょうか?
「数字はこのファイルから読み込んで」と言われたら?

Make every program a filter

書籍「UNIXという考え方」に

「9. Make every program a filter. すべてのプログラムをフィルタとして設計する」

という章が登場します。(短い本ですが、非常に良い本なので一読をオススメします。)

https://www.amazon.co.jp/dp/4274064069

この考え方によると、

すべてのプログラムは、何らかの形式のデータを入力として受け入れ、何らかの形式のデータを出力として生成する。プログラムはデータを作らない。人間がつくる。

とあります。

FizzBuzzプログラムで考えてみると、1〜30や100〜130の部分は人間が決めているのでデータだと言えます。「Fizz」や「Buzz」を表示するのは機械的なロジックです。

データに当たる部分はプログラムから追い出してしまいましょう。これが今回言いたかった「データとロジックを分割する」にあたります。

fizzbuzz.rb
while line = gets     # 標準入力から1行読み込む
  i = line.chomp.to_i # 標準入力からのデータは文字列なので数字に変換する
  if i % 15 == 0
    puts "FizzBuzz"
  elsif i % 5 == 0
    puts "Buzz"
  elsif i % 3 == 0
    puts "Fizz"
  else
    puts i
  end
end

これでデータをプログラムから追い出すことができました。
これにより、データの生成はプログラムと関係なくなり、数字を変える毎にプログラムを修正することもなくなります。データの与え方も自由です。

seq 30 | ruby fizzbuzz.rb # 1〜30のfizzbuzz
cat data.txt | ruby fizzbuzz.rb # 保存しておいたファイルから読み込む
echo 12345 | ruby fizzbuzz.rb # 12345は何になる?

まとめ

いかがでしたか?たったこれだけで、いつものプログラムがイケてる感じになり、やってい気は増大します。
今回はFizzBuzzの例でしたが、普段書くプログラムにもあてはめて考えてみてください。

「今回やりたいことのデータはなんだろう?」
「このプログラムはデータが変わったときに対応できる?」

そんな時に思い出してみてください。

「データとロジックを分割せよ」

読んで下さり、ありがとうございました。