プログラムの勉強

日経ソフトゥエア2008.09号が「例題を解きながら、実戦力を身につけよう! 楽しく学ぶプログラミング」という特集で、1題目が「3の倍数と3の付く数字だけ全角の漢数字(例:30なら三〇)で出力し、それ以外は半角数字で出力する」というもの。2題目が「二つの大きな整数値(25桁)の和と差を出力する」というものでした。話の進め方は、生徒が回答(Cのプログラム)を提出し、先生が添削する、生徒が納得する、という流れです。3題目は「36進法で書かれた数字を受け取り、10進数に変換して出力する」というものでした。問題4は数字から36進法への逆変換。問題5は素因数分解。問題6はコメント行の抜き出し。


よくわかりませんが、今の若い人はこういうのでプログラムの入門をするのか。大変だね。と思いました。わたしはCはやらないので、Rubyで回答をなぞりながら読んでいます。だいたいがプログラムは趣味ですので、ちゃんとというか人に教えてもらって勉強をしたことがない。でも思うのだけれど、やっぱこんな問題突然出されて基礎の基礎だから自分で考えろったってできなくて当たり前だよねー。FizzBuzz問題のプログラムができないプログラマーがいるというので大騒ぎになったことがあるけど、いろんなコメント読んでいて違和感ありました。自分はできるけど、できるようになった自分がどうしてできるようになったかをよーく考えると能力じゃないよね。学習ですよね。学習生産性の話だと思う。ということは教育生産性でもあるわけで、できるできない、バカ利口、学歴云々より、知っている知っていない、経験があるない、覚えている忘れちゃったという方により近いと思う。さはさりながら、過去の課題に似てはいるけどあたらしく解きつづけなければいけない現実がありますので、能力化しなければいけない。


上記の課題を解く際、わたしは課題理解を含め、テストから先に書き始めましたが、これってRubyだからでしょうか? だいたい何言語で書き始めるにしてもその言語を思い出すのに時間がかかるので、テストを書いてエラーを出しながら書き進んだ方が思い出しが速いと思う。とかね。あとでまた書きます。


素数はホントはむずかしい問題だと思いますが、とりあえず、次のようなテストを書きます。

# test_facts.rb
require 'test/unit'
require 'facts'

class Tests < Test::Unit::TestCase
  def test_primesupto
    assert_equal([2], primesupto(2))
    assert_equal([2, 3, 5], primesupto(5))
  end

  def test_factorization
    assert_equal([5], factorization(5))
    assert_equal([2, 2, 3], factorization(12))
  end
end

つまり、ある数の素因数分解をする以上、その数までの素数の配列をつくるメソッドが要るでしょうということで、primesuptoというメソッドを考える。そこに例えば、2とか5とか入れると[2]とか[2,3,5]と返してくる。それを使って素因数分解をするわけですが、小さい数から順番に割って余りがゼロなら割った数を配列に入れていく。

これを走らせると、facts.rbがありません、と言ってくるので、facts.rbというファイルを作って走らせると、今度はprimesupがありません。そこで中味なしのdef primesupto; endだけ書いて、またテストを走らせる、というように進みます。失敗(failure)もエラーも出なくなったらとりあえず完成。完成後に無駄を省く作業やなんかをする。少しでも触ったらテストを走らせる。必要に応じてテストを加えていく。下のはとりあえずレベルのものです。

 1: #!/usr/bin/ruby 
 2: 
 3: def primesupto(n)
 4: ary = (2..n).to_a
 5: primes = []
 6: 
 7: ary.size.times { |i|
 8:   counter = i+1
 9:   while counter < ary.size
10:   ary[counter] = 0 if  ary[i] != 0 && ary[counter] % ary[i] == 0
11:   counter += 1
12:   end
13: }
14: ary.delete(0)
15: return ary
16: end
17: 
18: def factorization(n)
19: number = n
20: ary = []
21: for i in primesupto(number) do
22:   while n % i == 0
23:     ary.push(i)
24:     n = n / i
25:   end
26: end
27: return ary
28: end

ここまでするのに100個くらいエラーを出しながらやりますが、頭はあまり使わない。多分今回の記事を読んで理解するのより簡単。これでp factorization(96)とやると[2,2,2,2,2,3]という配列が返ってきます。課題はこれを2x2x2x2x2x3とアウトプットせよ、ということですが、個人的には[[2, 5],[3, 1] ]と出したい。あと、96なら3までで終わっているのに、96まで割り続ける無駄があります。


2時間くらいで出来上がっています。この程度の回答を作るのに2時間は長い、短いは意見があると思いますが、テスト込みですので、生産性は悪くないと思う。それよりなにより、学習生産性が高い。プログラムを指で追いながら頭の中でシミュレーションしながら作るより、枠組みを書き、枠組みの部分はエラーメッセージの言うままに作っていって、ホントのロジックの部分だけ頭で考える。ちょっと考えたら実験。ある意味あまり頭がよくなくても作れます。文系用と言っていいかもしれない。


いかがでしょうか。