今日は天気がよかったけど・・・

 朝ニュースで横横道路が凍結のため全線通行止めということで、結局仕事を休みにしました。あちらこちらでスリップが原因の大きな事故がおきていたので、行こうと思えば下道で行けたと思うけど、多分行かなくて正解だったと思います。

 

 さて、今日は午前中は部屋の掃除、午後はちょっと買い物に出かけましたが、かなりの時間をrubyの勉強に使いました。「たのしいruby」は、rubyの本の中ではかなりわかりやすいよい本(というか、rubyには、ほかの言語ほど丁寧な解説本はない)だと思うのですが、それでもかなり高度な本です。で、やっぱりどれだけできる人がわかりやすい本を書いても、残念ながらどうしても書き漏らしがあるようです。引っかかる部分って、人それぞれというのもあると思うのですが、たとえば、普通はこんな使い方をするんだけど、こんな使い方もある。だけど、一般的に解説されてない、といったようなケース。スクリプトを読んでいるうちに、ふとしたきっかけで気がつくこともありますが、どこかで解説してないと一生気がつかないかもしれないようなコードが書いてあったりして、かなり苦戦しています。

 昨日から、郵便番号のcsvファイルを読み込んで、データベースを作成し、検索結果を表示するという事例に取り組んでいるのですが、こういうスクリプトが書いてあります。(「たのしいruby 第2版」p429より引用)

 

#jzipcode.rb

require "gdbm"
require "csv"

module JZipCode
  COLUMN_ZIP = 2
  ZIP_FILE = "ken_all.csv"
  DB_FILE = "ken_all.db"
  
  module_function
 
  def make_database(zipfile,dbfile)
    return if File.exist?(dbfile)
    open(zipfile){|io|
      GDBM.open(dbfile, 0644, GDBM::NEWDB){|db|
        io.each{|line|
          colums = line.split(",")
          zipcode = colums[COLUMN_ZIP].delete('"')
          
          if tmp = db[zipcode]
            line = tmp + line
          end
         
          db[zipcode] = line
         
        }
      }
    }
  end

  def find(code)
    make_database(ZIP_FILE, DB_FILE)
    GDBM.open(DB_FILE, nil, GDBM::READER|GDBM::NOLOCK){|db|
      if line = db[code]
        return CSV.parse(line)
      end
    }
    return nil
  end
end

 問題は、赤字の部分です。どこにもtmpという変数は設定されておらず、いきなり現われたtmpとdb[zipcode]を比較して、条件分岐をしているように見えます。ここで昨日の夜からずっと悩んでいたのですが、どうやらここでのifは、「もし、db[zipcode]がnilでなかったら、tmpにdb[zipcode]を代入して、次の行を実行する。」というスクリプトのようです。何度もifのところを読み返してみたのですが、この使い方はまったく触れられていませんでした。このスクリプト、実は下の方にreturn nilという部分(緑)があって、ちょっと上のif line = db[code]のところで該当がないcodeを入力するとnilを返します。上の赤の部分は実はnilでない時のみ実行される部分で、通常は登録されていないzipcodeが与えられるので、この部分は実行されず、その下のdb[zipcode] = lineが実行されて、データベースにkey、valueの順にデータが登録されていくスクリプトなのです。

 次のスクリプトはjzipcode.rbをモジュールとしてインクルードして実行するスクリプトです。

 

#test_jzipcode.rb(p433より引用)

require "jzipcode"
require "nkf"

t0 = Time.now
code = "2291133"

if rows = JZipCode.find(code)
  rows.each{|row|
    address = row[6] + row[7]
    unless /^\210\310\211\272\202\311/ =~ row[8]
      address << row[8]
    end
    puts NKF.nkf("-s", address)
   
   
  }
end
p Time.now - t0

 このスクリプトでも赤の部分でいきなりrowsという変数が現われています。これも、JZipCode.find(code)がnilでなければrowsに戻り値を代入するというケースです。

 わかってしまえば、何のことはない。そんなスクリプトですが、これがわかるまでかなり苦労しました。プログラミングの世界は私にとって、ドラクエでいうと、最初のお城からようやくフィールドに出て歩き始めた、そんなところなのでしょう。レベルアップを図り、キメラやドラゴンと戦えるようになるのは、まだまだはるか遠い先の話のようです。果たしてすらいむ1号は勇者になれるのか?