ページをどんどん繰る再起

昨日のTwitterの続き。フォローしている人のリストはAPIを叩けばXMLで持ってこれるけど、1度に戻ってくるのは100人単位。100人以上をフォローしている人を対象とする場合、「user?page=2」とかやって次々とページを繰っていかないといけない。それで、次のように書き換えてみた。

require 'cgi'
require 'net/http'
require "rexml/document"

$KCODE='u'

user = ARGV.shift

def get_friend_list(user, page, list)

  option = "?page="+page.to_s

  Net::HTTP.start('twitter.com', 80) {|http|
    response = http.get("/statuses/friends/#{user}.xml"+option)
    doc = REXML::Document.new response.body
    doc.elements.each("*/user/name") {|element|
      list << CGI.unescapeHTML(element[0].to_s)
    }
  }
  if (list.size % 100 == 0)
    get_friend_list(user, page + 1, list)
  else
    list
  end
end

puts get_friend_list(user,1,[])

最初、listをグローバル変数($list)にして出てきたユーザー名をどんどんリストに書き足していってたけど、なんか違うなと思って「再起」でググッてみた。そっか、再起呼び出しのキモって、関数を呼び出すときに後腐れなく加工中のデータを渡すってことだったかと気づいてローカル変数listにしたら、すっきりした。毎回必要な分を渡して呼び出すから、スタックを積み残したままにしないで捨てる文末再起最適化ができるってことかな。

あれ、再起っぽく書いて何がうれしいんだっけな。うーん、pageをインクリメントしたりしてループを書かなくていいこと? whileとかにしないで完結してること? 実はフィボナッチ以外の再起って初めて書いたかも。

それにしても、Twitter APIのように具体的に例があると、確かに楽しいし、俄然やる気になる。教本のサンプルを入れて試してだと、何となく気が重くなる日があるし。

あ、よく見たらバグがある。フォローしている人の数が100とか200とかキリのいい数字の人だと、存在しないページのリクエストを送ってしまう。何が起こるんだろうか。