Rubyで配列に対して重複を許す順列を列挙するメソッドを再帰で実装したが標準にもうありました
要するにこういうのの出力する要素数によらない版を作りたかった
(出力する要素数だけブロックを入れ子にしないといけないので)
def naive_enumerate array array.each do |a_1| array.each do |a_2| array.each do |a_3| yield [a_1, a_2, a_3] end end end end arr = [1, 3, 5] naive_enumerate arr do |a| p a end
出力結果はこんなかんじ
[1, 1, 1]
[1, 1, 3]
[1, 1, 5]
[1, 3, 1]
[1, 3, 3]
[1, 3, 5]
[1, 5, 1]
[1, 5, 3]
[1, 5, 5]
[3, 1, 1]
[3, 1, 3]
[3, 1, 5]
[3, 3, 1]
[3, 3, 3]
[3, 3, 5]
[3, 5, 1]
[3, 5, 3]
[3, 5, 5]
[5, 1, 1]
[5, 1, 3]
[5, 1, 5]
[5, 3, 1]
[5, 3, 3]
[5, 3, 5]
[5, 5, 1]
[5, 5, 3]
[5, 5, 5]
で、こういう実装をしてみたが
def enumerate_error array, n, buf=[] if n == 0 yield buf else array.each do |a| enumerate_error array, n-1, buf + [a] end end end arr = [1, 3, 5] enumerate_error arr, 3 do |a| p a end
`enumerate_error': no block given (LocalJumpError)というエラー発生
再帰的に呼び出してる所がブロック付きメソッド呼び出しになってないからですね
ブロックに渡された要素をまんま渡すよう実装
def enumerate array, n, buf=[] if n == 0 yield buf else array.each do |a| enumerate(array, n-1, buf + [a]) {|b| yield b} end end end arr = [1, 3, 5] enumerate arr, 3 do |a| p a end
で、無事完成したが、Ruby 1.9.2 からもう標準で実装されていたようです
arr = [1, 3, 5] arr.repeated_permutation(3) do |a| p a end
Array#permutation (Ruby 1.8.7から)
Array#combination (Ruby 1.8.7から)
Array#repeated_permutation (Ruby 1.9.2から)
Array#repeated_combination (Ruby 1.9.2から)
といたれり尽くせりのようです
http://ref.xaio.jp/ruby/classes/array
ランニングをしながら学んだ事を頭の中で復習しよう
兄pod(兄にipodを譲渡する事)のせいでランニング中にpodcastが聞けなくなったので、どうにもヒマだと思ってたんですが良い方法を思いつきました。今日学んだ事を思い出して復習すればいいんです。
受験期にはよく帰り道の電車の中で勉強した事を思い出して復習してましたが、最近その習慣がなくなっていました。自分で実践してみて思う感想なのですが、頭の中で思い出すというのはとても良い勉強になります。
まず、自分で良く理解して整理されていないとうまく思い出す事が出来ません。これが起こるたびに「理解してねぇじゃんww」と反省し、時にはどうしても気になって参考書を調べなおすという事もよくやりました。
それと、何回も思い出して反芻するうちにだんだんとそのスピードが速くなり、最終的には一瞬で体系だった知識を思い出せるようになります。まぁ数日たてば出来なくなりますが、かなり長期に渡って知識を記憶しておく事が出来ます。
丁寧語やめよう、なんか肌に合わねぇ。
で、電車の帰り道とかだと疲労困憊してて、今まで勉強してたのに気が休まらねぇとか考えてしまうけど、ランニング中だとむしろ逆の思考になる。
出来る限りランニングとは別の事を考えて、気を紛らわしたいと思うようになる。
うまく気を紛らわすためにipod使ってたけど、必要なかったんや!これでランニングの習慣も続けられそうだし、勉強も捗りそうだ。
皆さんも是非お試しあれ。
さらに赤面ソースコードをリファクタリングしてみた
前回のソースコードにアドバイスをもらったのでさらにリファクタリングしてみました。
このリファクタリングする前にスクリプト走らせる前のyes/noのチェッカやら付けてクラスが肥大化してたのでMechanizeの部分だけをクラス抽出してます。
#encoding:utf-8 require 'rubygems' require 'mechanize' require 'kconv' class IntentLotteryUnit INTENT_URL = 'http://www.hyogo-park.or.jp/yoyaku/intention/auth.asp?ch=0' LOGOUT_URL = "http://www.hyogo-park.or.jp/yoyaku/kaiin/logout.asp" def initialize @agent = Mechanize.new end def check_unit(id, pass, name, date, range_time, out_file) access login(id, pass) write_down_already_verified(id, pass, name, out_file) verify_intent(id, pass, name, date, range_time, out_file) logout end private def access @agent.get(INTENT_URL) end def login(id, pass) @agent.page.form_with(:name => 'form1') do |f| f.field_with(:name => 'mem_number').value = id f.field_with(:name => 'mem_password').value = pass f.click_button(f.button_with(:value => ' 次 へ ')) end end def write_down_already_verified(id, pass, name, out_file) @agent.page.search('tr/td/table[@width="400"]')[0..50].each do |p| if /予約は承認されました/ =~ p.inner_text date_time = capture_date_time(p) write_down(date_time, id, pass, name, "予約済み\n", out_file) end end end def verify_intent(id, pass, name, date, range_time, out_file) approvals = @agent.page.links_with(:text => '予約承認確認') approvals.each do |apr| apr.click date_time = capture_date_time_in_aprove write_down(date_time, id, pass, name, "\n", out_file) if date == date_time[0] && range_time == date_time[1] @agent.page.form_with(:name => 'form1') do |f| f.submit(f.button_with(:name => 'submi2')) end end end end def logout @agent.page.link_with(:href => LOGOUT_URL).click end def capture_date_time_in_aprove p = @agent.page.at('form/table[@width="400"]/tr/td/table[@width="400"]') capture_date_time(p) end def capture_date_time(p) date_time = [] date_time << $1 if /([0-9]*年[0-9]*月[0-9]*日)/ =~ p.inner_text date_time << $1 if /([0-9]*時〜[0-9]*時)/ =~ p.inner_text end def write_down(date_time, id, pass, name, eol, out_file) line = [date_time, id, pass, name, eol].join(',') puts(line) out_file.print(line) end end
友人にコード見てもらってすごいコードがすっきりしてうれしかったけど、少しくやしい。
verify_intent()と言いつつも意思確認をまだ行っていない日付を書き下す処理も入ってるのでここらへんもどげんかせんといかん。
追記:RubyのMechanizeはkitamomongaさんの記事
RubyのMechanizeを解説 for 1.0.0で学びました。そろそろお礼をせんといかん。
前回の赤面ソースコードをリファクタリングしてみました。
学校の課題にリファクタリングがあったのと、いいかげんこれはひどいという事で前回の赤面ソースコードをリファクタリングしてみました。
とりあえずクラス化、バカ長いメソッドcheck_unit()を細分化したのと、ページの文字列検索をcapture_date_time()に、文字列の標準出力とファイル出力をwrite_down()へ共通化しました。
Clean Code アジャイルソフトウェア達人の技に「関数は第一に短く、第二にもっと短く」ってあったけど、なるほど頭スッキリする。
#encoding:utf-8 require 'rubygems' require 'mechanize' require 'kconv' class IntentLottery def initialize @agent = Mechanize.new end def check_only(filename_id, filename_out) out_file = open(filename_out,"w") open(filename_id).each_line do |line| id, password, name = line.split(/,/) check_unit(id,password,name,'tekitou','tekitou2',out_file) end out_file.close end def check_and_regist(filename_intent, filename_finish) finish_file = open(filename_finish,"w") open(filename_intent).each_line do |line| if(/----/ =~ line) then puts ("next day") next end date,range_time,id,password,name = line.split(/,/) check_unit(id,password,name,date,range_time,finish_file) end finish_file.close end private def check_unit(id, password, name, date, range_time, out_file) @agent.get('http://www.hyogo-park.or.jp/yoyaku/intention/auth.asp?ch=0') authenticate(id, password) write_down_already_verified(id, password, name, out_file) verify_intent(id, password, name, date, range_time, out_file) @agent.page.link_with(:href => "http://www.hyogo-park.or.jp/yoyaku/kaiin/logout.asp").click end private def authenticate(id, password) @agent.page.form_with(:name => 'form1'){|f| f.field_with(:name => 'mem_number').value =id f.field_with(:name => 'mem_password').value = password f.click_button(f.button_with(:value => ' 次 へ ')) } end private def write_down_already_verified(id, password, name, out_file) @agent.page.search('tr/td/table[@width="400"]')[0..50].each do |p| if(/(予約は承認されました。)/ =~ p.inner_text)then date_time = capture_date_time(p) write_down(date_time, id, password, name, "予約済み\n", out_file) end end end private def verify_intent(id, password, name, date, range_time, out_file) approvals = @agent.page.links_with(:text => '予約承認確認') approvals.each do |apr| apr.click date_time = capture_date_time_in_aprove write_down(date_time, id, password, name, "\n", out_file) if (date == date_time[0] && range_time == date_time[1]) then @agent.page.form_with(:name => 'form1'){|f| f.submit(f.button_with(:name => 'submi2')) } end end end private def capture_date_time_in_aprove arr = Array.new p = @agent.page.at('form/table[@width="400"]/tr/td/table[@width="400"]') arr = capture_date_time(p) return arr end private def capture_date_time(p) arr = Array.new if(/([0-9]*年[0-9]*月[0-9]*日)/ =~ p.inner_text) then arr << $1 end if(/([0-9]*時〜[0-9]*時)/ =~ p.inner_text) then arr << $1 end return arr end private def write_down(date_time, id, password, name, eol, out_file) line = Array.new line << date_time line << [id, password, name, eol] puts(line.join(',')) out_file.print(line.join(',')) end end
シンタックスハイライト反映されないし、タブ8文字て…どうなってやがる…!?
追記:シンタックスハイライトに対応されたのことなので編集して更新、タブ8文字どうすればいいんだろ?
追記:タブをスペース2文字に置換しました
rubyのmechanizeライブラリを用いたとあるテニスコートのコート予約自動化スクリプト(の一部)赤面ソースコード公開
大学のIT技術講演会で株式会社はてなの人に触発され、株式会社はてなの回し者である友人 @y_uuki に招待されるがまま
ブログを始めました。
はてな記法モードというものでソースコードを綺麗に表示出来るとのことなのでとりあえず
rubyのMechanizeライブラリを用いたとあるテニスコートのコート予約自動化スクリプト
(の一部、テニスコートの抽選予約に選ばれたものに対して「予約します」という意思確認を行う処理の自動化)のソースコードを公開します。
因にputs("Usage:intentioncheck.rb input/id.txt output/filename_out")となっていますが、読み込むファイルid.txtは
"ID,パスワード,名前(改行)"を80人分書いてあります。
かなり汚いコードなので書き方の添削をしていただけるとありがたいです。
# encoding: utf-8 require 'rubygems' require 'mechanize' require 'kconv' def check_unit(id,password,name,date,range_time,out_file) agent = Mechanize.new() #if(agent.get('http://www.hyogo-park.or.jp/yoyaku/intention/auth.asp?ch=0'))then #if(agent.get('http://www.hyogo-park.or.jp/yoyaku/intention/auth.asp?ch=0'))then # $stderr.print("Error:Term is over\n") # exit #end agent.get('http://www.hyogo-park.or.jp/yoyaku/intention/auth.asp?ch=0') agent.page.form_with(:name => 'form1'){|f| f.field_with(:name => 'mem_number').value =id f.field_with(:name => 'mem_password').value = password f.click_button(f.button_with(:value => ' 次 へ ')) } #http://www.hyogo-park.or.jp/yoyaku/intention/intention_check.asp agent.page.search('tr/td/table[@width="400"]')[0..50].each do |p| if(/(予約は承認されました。)/ =~ p.inner_text)then if(/([0-9]*年[0-9]*月[0-9]*日)/ =~ p.inner_text) then print($1,(',')) #puts p.inner_text out_file.print($1,(',')) end if(/([0-9]*時〜[0-9]*時)/ =~ p.inner_text) then print($1,(',')) #puts p.inner_text out_file.print($1,(','),id,(','),password,(','),name,(',')) end puts("予約済み\n") out_file.print("予約済み\n") end end approvals = agent.page.links_with(:text => '予約承認確認') approvals.each do |apr| apr.click s_date = 'init' agent.page.search('div')[0..50].each do |p| if(/([0-9]*年[0-9]*月[0-9]*日)/ =~ p.inner_text) then print($1,(',')) s_date = $1 #$&.clone #puts p.inner_text out_file.print($1,(',')) end if(/時〜/ =~ p.inner_text) then puts p.inner_text search_time = p.inner_text if(date == s_date && range_time == search_time)then puts('match so resist') agent.page.form_with(:name => 'form1'){|f| f.submit(f.button_with(:name => 'submi2')) } puts agent.page.uri puts('resitered') end out_file.print(p.inner_text,',',id,',',password,',',name,("\n")) end end end end def check_only(filename_id,filename_out) if(filename_id && filename_out) then else puts("Usage:intentioncheck.rb input/id.txt output/filename_out") return end id_file = open(filename_id) finish_file = open(filename_out,"w") while(id_text = id_file.gets) do id_array = id_text.split(/,/) puts id_array[2] id_array[2].chomp! id = id_array[0] password = id_array[1] name = id_array[2] begin timeout(30){ check_unit(id,password,name,'tekitou','tekitou2',finish_file) } rescue Timeout::Error retry end end id_file.close finish_file.close end def check_and_regist(filename_id,filename_out) id_file = open(filename_id) finish_file = open(filename_out,"w") while(id_text = id_file.gets) do if(/----/=~id_text)then print(id_text.chomp," next day----\n") next end id_array = id_text.split(/,/) date = id_array[0] range_time = id_array[1] id = id_array[2] password = id_array[3] name = id_array[4].chomp print(date,range_time,name,"\n") check_unit(id,password,name,date,range_time,finish_file) end id_file.close finish_file.close end #if(ARGV[2])then # check_and_regist(ARGV[0],ARGV[1]) #end #if(ARGV[1])then # check_only(ARGV[0],ARGV[1]) #else # puts("Usage:intentioncheck.rb input/id.txt output/filename_out") # puts("Usage:intentioncheck.rb input/int_file output/filename_out -resist") #end