Posted at

Ruby: Enumerator::Lazy で URI生成

More than 3 years have passed since last update.

末尾に「/page=n/」(n はページ番号)がつくような URI を生成したい場面の話です。


Enumerator::Lazy で URI生成

URI生成の簡単な例です。


sample1.rb

mk_uri_gen = -> n = 0 { -> { "http://hoge/hoge/page=#{n += 1}/" } }

ug = mk_uri_gen.()
p ug.() #=> "http://hoge/hoge/page=1/"
p ug.() #=> "http://hoge/hoge/page=2/"
p ug.() #=> "http://hoge/hoge/page=3/"
:


これは、Enumerator::Lazy の仕組みを使い以下のようにできます。


sample2.rb

ug = (1..Float::INFINITY).lazy.map{|n| "http://hoge/hoge/page=#{n}/" }

p ug.next #=> "http://hoge/hoge/page=1/"
p ug.next #=> "http://hoge/hoge/page=2/"
p ug.next #=> "http://hoge/hoge/page=3/"
:


Enumerator::Lazy を使う場合は rewind や take, each などが使えて便利です。

   :

p ug.next #=> "http://hoge/hoge/page=4/"
p ug.next #=> "http://hoge/hoge/page=5/"
ug.rewind # 巻き戻し
p ug.next #=> "http://hoge/hoge/page=1/"
p ug.next #=> "http://hoge/hoge/page=2/"
:

# 最初の5つ分の繰り返し

ug.take(5).each do |uri|
p uri
end

# 無限の繰り返し (注)ずっと処理が続きます

ug.each do |uri|
p uri
gets # ここで、キー押下待ちになります。処理をやめるときは Ctrl-C を押しましょう
end


URI テンプレートのパラメタ化

URI のテンプレートをパラメタとして渡せるようにしたいとします。

パーセント記法による書式文字列を渡す方法が考えられます。


sample3.rb

hoge = 'http://hoge/hoge/page=%{n}'

mk_uri_gen = -> tmpl { (1..Float::INFINITY).lazy.map {|n| tmpl % {n:n} } }

ug = mk_uri_gen.(hoge)
p ug.next #=> "http://hoge/hoge/page=1/"
p ug.next #=> "http://hoge/hoge/page=2/"
p ug.next #=> "http://hoge/hoge/page=3/"
:


固定文字列をパラメタにしたい場合は上のようになりますが、ラムダを渡す方がより柔軟に処理できます。


sample4.rb

          # 1ページ目以前には「/page=n/」をつけない URI を生成するラムダ

hoge = -> n { %(http://hoge/hoge/#{"page=#{n}/" if n > 1}) }

mk_uri_gen = -> tmpl { (1..Float::INFINITY).lazy.map(&tmpl) }

ug = mk_uri_gen.(hoge)
p ug.next #=> "http://hoge/hoge/"
p ug.next #=> "http://hoge/hoge/page=2/"
p ug.next #=> "http://hoge/hoge/page=3/"
:



Nokogiri::HTML::Document を返すジェネレータの例


sample5.rb

require 'open-uri'

require 'nokogiri'

hoge = -> n { %(http://hoge/hoge/#{"page=#{n}/" if n > 1}) }

conv = -> uri { Nokogiri::HTML open uri }
mk_doc_gen = -> tmpl { (1..Float::INFINITY).lazy.map(&tmpl).map(&conv) }

dg = mk_doc_gen.(hoge)
# dg は Document を返すジェネレータ
dg.each do |doc| # doc は URIが指定するページを読み込んだ Nokogiri::HTML::Document オブジェクト
puts (doc/:title).map(&:text) * ' '

gets # キー押下待ち
end



おわりに

本稿内容の動作確認は以下の環境で行っています。

$ ruby -v

ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-linux]

$ gem list nokogiri


*** LOCAL GEMS ***

nokogiri (1.6.5)

$ lsb_release -a

No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 14.04.1 LTS
Release: 14.04
Codename: trusty

参考