「Lisp脳」の謎に迫る – Schemeプログラマの発想という僕の好きな記事があるのですが、Rubyプログラマの発想も同じだなーと、ふと思いました。
データの加工として考える
1から100までの数をプリントするプログラムを書け。ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、3と5両方の倍数の場合には「FizzBuzz」とプリントすること。
記事ではこの実装の例(手続き型)として、こんなコードが載っていますが、
(do ((x 1 (inc! x))) ((>= x 100) x) (cond ((= (modulo x 15) 0) (print "FizzBuzz")) ((= (modulo x 5) 0) (print "Buzz")) ((= (modulo x 3) 0) (print "Fizz")) (else (print (x->string x)))))
これをRubyに書き直すと、こんなコードになるのではないでしょうか。
(1..100).each do |x| if x % 15 == 0 puts 'FizzBuzz' elsif x % 5 == 0 puts 'Buzz' elsif x % 3 == 0 puts 'Fizz' else puts x end end
でもRubyプログラマのコードでこんなコードは、めったに見ません。大抵はSchemaプログラマの発想と同じく、
「とりあえず1から100までのリストを作れば良さそうだ」
と考えます。つまりここからがスタートです。
(1..100).to_a # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
「mapを使ってこのリストを加工して、別のリストを返せばいいじゃん」
これは「mapを使ってこの配列を加工して、別の配列を返せばいいじゃん」って考えますね。
class Fixnum def fizzbuzz return 'FizzBuzz' if self % 15 == 0 return 'Buzz' if self % 5 == 0 return 'Fizz' if self % 3 == 0 self end end (1..100).map(&:fizzbuzz) #=> [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz", 16, 17, "Fizz", 19, "Buzz", "Fizz", 22, 23, "Fizz", "Buzz", 26, "Fizz", 28, 29, "FizzBuzz", 31, 32, "Fizz", 34, "Buzz", "Fizz", 37, 38, "Fizz", "Buzz", 41, "Fizz", 43, 44, "FizzBuzz", 46, 47, "Fizz", 49, "Buzz", "Fizz", 52, 53, "Fizz", "Buzz", 56, "Fizz", 58, 59, "FizzBuzz", 61, 62, "Fizz", 64, "Buzz", "Fizz", 67, 68, "Fizz", "Buzz", 71, "Fizz", 73, 74, "FizzBuzz", 76, 77, "Fizz", 79, "Buzz", "Fizz", 82, 83, "Fizz", "Buzz", 86, "Fizz", 88, 89, "FizzBuzz", 91, 92, "Fizz", 94, "Buzz", "Fizz", 97, 98, "Fizz", "Buzz"]
もっといろんな処理(例えば2で割り切れる数をnilにする)を加えたければ、この後ろにmapで処理を重ねていけば良いのです。
(1..100).map(&:fizzbuzz).map(&:nanika)
データを加工する部分をメソッド化して再利用できるようにしておく、というのは割とポピュラーですよね。
Rubyプログラマの人は関数型言語の経験がなくても、結果としてSchemaプログラマのような考え方をしているのが面白いなーと思いました。