はてなキーワード: defとは
Pythonの方が弄れる対象が多いのに、なんでウェブ系だとPHPの方が流行ってんだろ
端末からのテキスト処理も楽だし、数値計算周りのライブラリも充実しているのに
PHPが優遇されているのって歴史的な経緯以外に何か他の理由でもあるのか?
けどまぁ、情弱な文系SEが大半を占めているバカだらけの日本じゃ別にPHPで困ることもないか
数値計算や端末からのテキスト処理なんてWeb系じゃ大して使わないからなあ…
Pythonに関しては、ZopeさえコケていなければWebサーバ用LLとして大成功していたはずなのに、
Railsなんかが登場したおかげで、すっかり影が薄くなってしまいますた....
ってか、railsにインスパイアされたフレームワークって今じゃ幾らでもあるよね
djangoとかCakePHPとか。rubyってRoRを使いたいユーザを除くと、
pythonやPHPの方がユーザー数は圧倒的に多いと思うんだけど
本家のrailsって、他を遥かに越えるほど良いものなんだっけ?
44
Zopeが登場した当時、「RDB+PHPはもう古い、これからはOODB+ZopeがWebの中軸になる!」と
少なくとも自分はZopeからPythonという言語を知ったし、その時点でRubyは知らなかった
そして、その後のORM(RDB)+Railsの出現と華々しい革新性への注目は、誰もが知っているだろう
今でもZopeの開発は継続されてはいるが、結果的に当初の期待が大きく裏切られたという事実は動かしがたい
djangoとCakePHPについては実際に触っていないので憶測になるが、おそらく技術水準ではRailsと同等だろう
しかしRailsはRailsでコミュニティの活動が活発だし、その進化は異常に早い
Railsに何か致命的なトラブルが発生して開発が停滞する、あるいはdjangoやCakePHPから
何かのイノベーションが提示されでもされない限り、後発のdjangoやCakePHPがRailsに追いつくのは無理
Railsは決して技術的に完璧なWebフレームワークではないんだけどね....(たとえばSeaSideのような.... )
だからこそ「もしもZopeが....だったなら」という「たら・れば」感はPythonコミュニティの潜在認識になっている
C a k e P H P は う ん こ
CakePHP使ってんの?
可哀そうにw
でもやっぱりいつもの使い慣れたLL(Python/Ruby)で
Webサービスを書きたいってのがある
求人数は
Ruby on Rails>>>>>>>>Django
http://www.indeed.com/jobtrends?q=django%2Cruby+on+rails&l=
どういうことなの?
求人数が多いのはそのためだと思うよ
なんかのミスかと思ったがアメリカでもRuby on Railsは人気があるのかなあ・・・
Pythonのほうが使いやすいと思うのだがフレームワークはRailsが優位なんだろうか
Djangoは周辺ライブラリが微妙だし本体も鈍くさい感じがする。
でも、FlaskはSinatraより好きだから、Pythonが嫌いってわけではない。むしろ好き。
ただ、いざ作り始めるとやっぱりRailsが楽だなあってなって、Railsを使い続けている。
同感だ
同じように思っている人が他にもいて安心した
PHPはフレームワークが乱立しすぎているから、RailsをPHPで実装してみようというやつが出てきた。
それに比べてPythonは、Zopeというデファクトスタンダードが既に存在していたけど、
ただ、どうやってもRailsもどきがRailsを超えることはできないのは間違いない。
パクリはオリジナルを超えられない(キリッ って定型句だけど、
これってキリッって言いたいだけだと思う。
D言語って超えたって?
B言語って超えたって?
PHPで同じ事をできないわけではないだろうけど、Ruby on Railsほど簡潔にはできない
まあくだらねえWEBサービス作って喜んでる情弱は早く死ねって事だよ
そういう理由じゃなくてRailsのほうが単純に情報もプラグインも多いからでしょ
linuxじゃデフォのツールなんだし、ツールとの連携を考えたらpython一択じゃん
わざわざ不合理で不完全な言語を使うなんて
もしも
>linuxじゃデフォのツールなんだし、ツールとの連携を考えたらpython一択じゃん
真実であるのなら、今頃はdjangoの情報とプラグインが溢れかえっているはず
yumや、gdbとgnomeの拡張がpythonであるからといって、それをwebアプリでも使いたいと思う人は少ないというだけのこと。
ソースからインストールする必要があったとしても、web開発ではrubyを使いたいという人が多いというだけのこと。
というか、世界中のPythonプログラマが Remeber Zope!! を合い言葉に
打倒RailsたるWebフレームワークを開発しているはずだけど、
Railsも登場してから、かなりの年月が経過しているんだけどなぁ....
その間にもRailsはRails 3が登場して、REST/AJAXの強化等の進化が継続しているよ
Ruby では
ary.map {|x| x**2}
map(lambda x: x**2, ary)
となり、lambda の本体が1つの式では表現しきれなくなると
.....
と書き換える必要があります。
f = lambda x:(x and f(x-1)*x)or 1
RubyにはPythonのように「lambda本体は式でなければならない」という限定がありませんから、
f = lambda{|x|if x == 0 then 1 else x*f.call(x-1) end}
または
f = lambda{|x|x == 0 ? 1 : x*f.call(x-1)}
と書けます。lambda内でreturnが使えますから、書きたければ
f = lambda{|x|if x == 0 then return 1 else return x*f.call(x-1) end}
でもOKです。
348
これはPythonをdisっているように見せかけてRubyをdisっているのか? と一瞬思ってしまったw
だってRubyのほうが長くない?CLのfuncallみたいなcall()がちょっとうざいし…
そしてどっちもlambda式の中で束縛変数の名前で再帰可能、と
print [x*2+100 for x in [1,2,3,4,5] if x > 2 and x < 5]
暗号のように見える。
puts [1,2,3,4,5].select{|i| i > 2 and i < 5}.map{|i| i*2+100}
思考の流れと、コードの流れが一致しているので書きやすい。
map(lambda x: x*2+100, filter(lambda x: x > 2 and x < 5, [1,2,3,4,5]))
pythonて可読性が高いのをうたってる割にはそこいまいちだよね
Rubyの場合には、左から右へと無名関数がデータフローあるいは
関数型プログラミングに不慣れな初心者でも、参照透明性のあるコードが自然に書ける
プログラマにとって優しい or プログラミングの楽しさを教えてくれるのがRuby
それと比較すると、Pythonのコードは、関数型プログラミングというものが
いかに高度で難解なものであるかという事をもったいぶってプログラマに押し付ける
もしもPythonしか知らないプログラマであれば、関数型 = 難解 という印象を持つだろう
階乗計算くらいだと単純すぎて、ナゼ重要なのかが分かりづらいと思うのでコードで示す
result_list = source_list.map { |elem|
x = foo(elem.x) # ここが局所宣言を書く部分
x + y # 最後に評価された式の値が、無名関数のリターン値になる
}
Rubyでは、map等に与える無名関数の中で局所的な環境(クロージャ)が作られるから、
x = foo(...) のような代入文がいくつでも(= 複雑な処理でも)書ける
このポイントは、実用的なプログラムを関数型風で書こうとした時に、威力を発揮する
余計分かりづらくなった
高卒ドカタなんだろうなぁと可哀想になる
集合の表記に似せてることが分かるから
355
>map/filterはfor/ifと同じだと言っているだけだから、難解という印象は持たない。
関数型プログラミングに慣れた、あるいは得意な人であれば、そういった印象なんだろね
Rubyの魅力はこれから関数型プログラミングを学ぼうとする初心者、 あるいはそんな初心者へ教える立場から見た、優しさ or 分かりやすさなんだ
[1,4,3,2].sort.reverse.map{|x| x.to_s}.join('-')
Pythonだと読みにくい。
'-'.join(map(str, reversed(sorted([1,4,3,2]))))
Pythonでは思考の流れと一致しないばかりか、「カッコだらけ」のコードになると.....
カッコだらけのコードを分かりやすくする基本的な方法は静的単一代入じゃないか
Rubyのやり方は基本ではなく玄人のやり方だろ
Pythonでは組み込みの型でメソッドチェインはやって欲しくないな
似たようなコレクションtuple,deque,array,queue等にも同じメソッドが必要になってくるし。
372
外部のライブラリでも列挙可能なものは、たいていEnumerableモジュールをimportしてますね
Rubyユーザーは列挙可能なものはmapやselectできて当然だろって思ってる気がします
Pythonは「何かを便利に書くためのしわ寄せ」をはっきり寄せてくる
得意と不得意を言語レベルではっきり主張するのでメリケン好みと言えなくもない
Rubyは全方位になんとなく八方美人なので、全体的になんとなく書きやすくてなんとなくキモくて遅い
「書きにくいってことはその処理に向いてないってことだから諦めろ」を地で行く
無名関数が文を使うほど複雑なら名前を付けるのが Python 流と想像。
これを読みづらいと感じるのは、左から右に流れる
もしかしてアラビア語ネイティブな人からすると逆に読みやすいのか?
トム・クルーズが若い頃、たしかトップガンの次の次ぐらいに出た作品で、アカデミー賞を取っている。
△△△
主人公のチャーリー(トム)は、父の遺産相続問題をきっかけに、自分が物心つく前から施設に入ってて存在すら知らなかった兄のレイモンド(ダスティン・ホフマン)と再会して、施設から無理やり連れ出してはみたものの、自閉症のレイモンドが次々にやらかす奇行にすっかり参ってしまう。飛行機で移動しようと空港に行けば、「ABC航空はダメ、19xx年x月x日どこどこで墜落○○人死亡。DEF航空?ノー!19xx年x月x日○○人死亡……」と百科事典のように正確な記憶力を見せて暴れ回る。
弟がいくら確率を持ち出して説得しても兄のパニックはおさまらない。
根負けしたチャーリーが数千キロもの行程を運転して行こうと高速に乗れば、今度はハイウエイの死亡事故統計を口走って「いやだー!死ぬー!」と絶叫するレイモンド。
「どうすりゃいいんだよ!」とキレたチャーリーは、名前のない田舎道ばかりを走って西をめざすことに。野原の真ん中のうらぶれた安宿で熱いバスにでもつかろうと湯の栓をひねると、「ダメーーー!そんなことしたら赤ちゃんが!」とまたしても半狂乱に。
△△△
映画だと、レイモンドのこの熱い湯船トラウマをきっかけにして幼かった兄弟の心の触れ合いの記憶が少しずつ呼び戻され、そこからしみじみと感動的なラストに向かって物語が動いていくのだけれど、別にここでなつかし映画語りがしたかったわけじゃない。
書きたかったのは、あの日以来、俺の嫁がレインマンになってしまったってことだ。
たぶん俺の嫁だけじゃない。日本中で、いや、遠く韓国や中国、欧州にも、突然たくさんのレインマンが生まれてしまったんだ。
△△△
「大丈夫だよ、この説明を見てみなよ」
「そうは言うけど、実際言われてるような事態にはなってないじゃん」
等々、いくら条理をつくして話し合ってもダメなのだ。
嫁は怖いのだ。
安心より、怖いという気持を選んだのだ。
だから、科学的にマユツバだと言われようと、自分の原始的な恐怖心にフィットするものばかりに注目し、耳を傾ける。そして言う。
「ほら、ご覧なさいよ!みんなそう言ってる」
嫁がブックマークしているサイトやフォローしているツイッターの発信者名を見て、俺は失神しそうになった。
俺のTLでは、「○ン○○」だの「○ル○」だのと認定されている名前がきら星のごとく並んでいる。そう、微生物がコロニーを作るみたいに、彼らはお互いに引かれ合って堅固なオピニオン集団を形成しているのだ。
嫁が俺じゃなくて○○○○や○o○の行進についていく。
マジかよ。
勘弁してくれ。
△△△
映画のチャーリーとレイモンドのような取っ組み合いにはならなかったけど、来る日も来る日も絶望的な言い争いが続いた。
わが家の食卓からはいくつかの食品が完全に姿を消し、子供は給食の牛乳にかえて水筒を持参するようになった。俺自身の親兄弟との関係もぎくしゃくしている。嫁が何か新しい心配の種を仕入れてくるたび、子供に新しい命令/禁止を下すたび、俺と嫁は決着のつかない論争を繰り返した。
△△△
でも。
俺には嫁を放り出すことはできなかった。
「愛してるから」かと聞かれても、正直うなずけない。
そんなきれいごとにしがみついていられるには、醜悪なものを見過ぎてしまった。
でも、俺がここで見切ったら、嫁には行くところがない。
子供は俺一人でもなんとかなる自信はあるが、子供は俺のことも嫁のことも愛している。
もちろん、俺も嫁も、それぞれの人として限られた能力の限りを尽くして子供を愛している。
△△△
四捨五入すればもう五十になろうっていうこの年で、いきなりレインマンと暮らす羽目になるとか、誰に想像できただろう。
ここで「考えや感じ方が違いすぎて落としどころさえ見つからない相手」をレインマンと呼ぶ俺には、全方向からごうごうたる非難が浴びせられるだろう。
嫁のコロニーからは「人をバカにするな!」と言われるだろうし、
俺のTLで気を吐いている闘士たちからは「ダスティン・ホフマンのようなほのぼのキャラに喩えるなどぬるいわ!」と批判されるだろう。
そしてなにより、現実に自閉症の人々と生活し、関わっている人たちからは、「偏見もいいところのとんだ愚か者」と指弾を受けるに違いない。この文章は、何の関係もないそれらの人々を引き合いに出す意図で書かれたものではないが、だからといって失礼が帳消しになるわけではない。そういった方々には心からお詫びしたい。
△△△
ただ。
映画の前半で「何やってんだよ!」ってキレまくって、少しでも兄に普通の行動を取らせようと無駄な努力にきゅうきゅうとしていたチャーリーが、レイモンドの目に映っている世界を、レイモンドの視点から理解して、彼に共感はできないものの、たった一人の肉親としてずっとそばにいようとするようになっていく過程を思った時、そこにはこれまでの俺の文字通りハゲになりそうな苦しみに、なんというか一条の光を投げかけてくれるヒントが潜んでいるんじゃないかって思ったのだ。
レインマンの奇行を迷惑がって、施設に追いやってしまうことも一つの道だけど、
そうはできない家族にとって、できることはなんだろう。
チャーリーは、自ら悩み苦しみながら、レイモンドへの愛を見つけて再生できた。
俺はまだ、嫁への愛を見つけ出していない。
△△△
うちのレインマンは、雨の日を極端に恐れている。
雨が降ると、家族全員に外出を禁止する。
雨じゃなくて、嫁に降り込められたうちの中で、俺はダスティン・ホフマンの顔をしたどこかのラスボスに祈りを捧げる。
――なあ、レインマン。
――ここに来て、歌ってくれよ。
――心に火傷を負った俺たちみんなのために。
RubyのSymbolと文字列の違いを研究室の輪講用に書いたのですが,折角なので公開したいと思います.
元々学部生に対する輪講用に書いた物なので,若干上から目線ですがご了承ください.
文字列とSymbolはよく似ています.プログラマから見ればどちらも文字列です.違いを一言で説明すると『プログラムが扱う』文字列か,『プログラマが見る』文字列かの違いです.例えば変数名は『文字列』ですが,rubyのオブジェクトである『文字列』ではないですよね?
例えば,今あなたがC言語でお絵描きライブラリを作っているとしましょう.その中に,色で塗りつぶすfillという関数があり,色を青・赤・緑の3色から選べ,fillの引数でそれを指定できるとしましょう.
fillの引数の設定方法として一番単純なのが,0を青,1を赤,2を緑として,0〜2で選択させる方法でしょう.しかし,それでは,どの数値が何色か覚えないといけないし,fill関数を知らない人から見れば,どういう意味の引数かすらさっぱり分かりません.
ではどうするかと言えば,普通はBLUE = 0, RED = 1, GREEN = 2と適当に定数を設定して,その定数を引数で指定させますよね.
こうして,fill関数の引数が意味の無い数値から意味のある文字列に変わったことによってプログラムが分かりやすい物となります.さて,ここで注意してほしいのは,ここで言う文字列はプログラムが扱うオブジェクトとしての文字列では無いということです.fill関数の引数として,"BLUE","RED", "GREEN"などとC言語の文字列を渡すということは普通しませんよね.それは,ここで言う文字列は,あくまでプログラマがプログラムコードを分かりやすくするために必要な文字列であって,プログラムがオブジェクトとして扱う(例えば,長さを求めるとかする)文字列ではないからです.
分かってきたでしょうか?
プログラムコード上では(つまりプログラマから見れば)どちらも同じ文字列(文字の列という意味で)ですが,実際に動くプログラムから見れば単なる数値と本物の文字列という大きな違いです.結局,fill関数の引数の具体的な値は何でもいいわけです.プログラマから見て文字列であればそれだけでよく,プログラムが動くときの実際のその中身は何でもいいわけです.これのために存在するのが,Symbolであり,:fooとひとたびSymbolを作成すれば:fooの実態は適当な数値となります.(この数値がいくらかなんていうことはもちろん気にする必要はありません)
そして,もちろん同じプログラム上では:foo == :fooはちゃんと成り立ちます.もうここまでくれば,Hashのkeyとして文字列でなくSymbolを使う理由が分かりますね.Hashのkeyはあくまで,プログラマが見る(プログラムコードを分かりやすくするための)文字列であってプログラムが扱うオブジェクトとしての文字列では無くて,keyの実際の値は何でもいい,からですね.(特別な場合を除いて)Hashのkeyに対してrubyのStringのメソッドを使うなんてことは無いですよね.
しかし,他の軽量言語ではSymbolなどなくHashのkeyとして普通に文字列を使うことが多いです.では,なぜrubyだけSymbolを使うのでしょうか.
その答えは一言でいうと,rubyの(プログラムコード上に直接書かれた,つまりリテラルの)文字列は他の言語と違いimmutable(不変)でない,からです.実際,pythonやjavascriptの文字列(リテラル)は破壊的に変更することはできませんが,rubyの文字列は破壊的に変更することができます. ('abc'.concat('d')の様に)
これがどういう違いを生むかというと,コード上に直接現れる文字列がimmutable(不変)であるならば,実行時に一つだけそのオブジェクトを作成し,後はそれを使いまわすという最適化ができます.
そうした時,Hashのkeyの様なプログラマから見た文字列というのは,プログラムコード上のリテラルとして現れるわけですが,これらは実行時に一つだけオブジェクトが作成され(特にコード上に現れる同じ文字列は全て一つのオブジェクトにまとめると),それらの比較はそれらに対する参照(そしてこれは大抵メモリのアドレスなど単なる数値)の比較で済むので,結局Symbolと同じ様な働きをするわけです.
本当はプログラマが見るためだけの文字列だけど,それをオブジェクトとしての文字列としても,Symbolと同じ様な働き,パフォーマンスが得られるならば,別にオブジェクトとしての文字列であってもいいわけです.
繰り返しになりますが,プログラマが見るためだけの文字列は,その中身・実態は何でもいいわけですが,その実態がオブジェクトとしての文字列でも十分なパフォーマンスが得られるならば,別にオブジェクトとしての文字列でもいいわけです.
さて,rubyに話を戻しますと,rubyはコード上に現れる文字列であっても,実行時にそのコードを通る度に毎回新たな文字列オブジェクトを作成します.
(以下のプログラムを動かすことで確認できる.)
def foo 'foo'.object_id end p foo, foo
つまり,rubyでは文字列が可変であるため,先に述べたような最適化ができない(または難しい)ので毎回新たな文字列オブジェクトが作成されるのです.
こうなると,先ほどの話とはうってかわって,プログラマが見る文字列はその実態は何でもいいのに,それを文字列リテラル(rubyのオブジェクトとしての文字列)にしてしまうと,毎回毎回文字列オブジェクトが作成されてしまうという非常にばかばかしい状況になってしまいます.我々はそれらの文字列オブジェクトに文字列としての操作は一切施さないのにも関わらず,です.
こういうわけで,rubyではプログラマが見るためだけの文字列にSymbolというruby特有のものを使うのです.
もちろん,プログラマが見るためだけの文字列を全て定数として(そしてもちろん中身は適当な値で)定義しても構わないわけですが,Hashのkeyとかで数多くのプログラマが見るためだけの文字列が現れることを考えると,とてもじゃないですけどそんなことは面倒でやってられないですよね.ですので,実行時に自動で適当な値にしてくれるSymbolというものが存在するのです.
以上で,Symbolについての説明を終えます.以下は蛇足です.
最初の方で出てきたfill関数をrubyで実装しようとしたとき,青・赤・緑の各色はその実際の値はなんでもいいのでrubyのSymbolを使って:blue, :red, :greenとしてもいいのですが,ライブラリとかでは大抵ちゃんと定数として定義されていることが多いです.
これは恐らく,定数として明示的に定義することで値の存在を明示でき,ドキュメント化の際にも役立つことによっているのでしょう.
しかし,あくまでこれは外部に公開するようなライブラリでの話であって,自分が使うちょっとしたプログラムならこういう場面でも精力的にSymbolを使っていってもいいと思います.ちなみに,僕ならSymbolを使います.
Symbolだと定義もいりませんし,定数は大文字ですから打つのが面倒ですし,あまりソースに大文字が入ると見た目がすっきりしません(主観).
Symbolは非常に便利なものですので,その意義・用途を十分に理解して,Hashのkeyにとどまらず様々な所で使えるようになりましょう.
2010年の日本のヒップホップ音楽 【作業用BGM】 ‐ ニコニコ動画(原宿) の曲目。
01. LOST IDENTITY - 道(TAO) (YouTube)
"Lost Identity", 08.15. ILL EAST RECORDS
02. 都市伝説 - 韻踏合組合 (YouTube)
"都市伝説", 10.10. IFK RECORDS
03. 極悪JAZZ feat. 鬼 - キリコ (YouTube)
Produced by DJ OLDFASHION
04. Black File 200回記念フリースタイル (Vimeo)
"Black File", 06.24. スペースシャワーTV
05. 夕方 - S.L.A.C.K. (YouTube)
"Swes Swes Cheap", 08.04. Dog Ear Records
06. 思考品M.T.B.D. feat B.D. - DJ Mitsu the Beats (YouTube)
"UNIVERSAL FORCE", 08.04. PLANET GROOVE
07. CAMP'S RULE - RAMB CAMP (YouTube)
Produced by DJ GQ
"RAMB CAMP", 05.14. FILE RECORDS
08. MECCHA MECHA - コッペパン (YouTube)
09. GOOD MORNING BABY - やけのはら (YouTube)
10. 捕食 - asa x CUBEc.u.g.p (YouTube)
"捕食-hoshoku-", 06.11. Jar-BeatRecord
11. イッちゃってイイ? (SUGAR CRACK REMIX) - SD JUNKSTA (YouTube)
12. TORA TORA TORA - TETRAD THE GANG OF FOUR (YouTube)
"SPY GAME", 11.12. FILE RECORDS
13. SUNDAYfeat. WAX - PONY (YouTube)
Produced by KURABEATS
"Verseday", 05.05. 桃源響RECORDS
14. Get Da Passports - Black Mont Blanc (YouTube)
15. 音楽ワルキューレ - DOTAMA (YouTube)
Produced by Fragment
16. いつもそう - WAX from SD JUNKSTA (YouTube)
Produced by KYN
"神成り", 07.21. 諭吉レコード
17. MADE IN JAPAN feat. X-MAN - L-VOKAL (YouTube)
Produced by 1689
"Lovin'", 03.03. MATENRO RECORDS
18. Homeward Journey - Uyama Hiroto (YouTube)
"Homeward Journey", hydeout productions
19. G.H.E.T.T.O - SHINGO☆西成 (YouTube)
Produced by TRAMP
20. STRAIGHT CASH - YING YANG (YouTube)
"TOTAL ECLIPSE", 10.13. YING YANG PRODUCTION
21. ILLMATIC RACER feat. 来門 - UNDER BROS (YouTube)
Produced by TAKE-C
22. トーキョー難民 - あるぱちかぶと (YouTube)
"◎≠", 02.03. Slye Records 在音
23. 森羅万象 - AXIS (YouTube)
"UNPOPULAR -在日外国人奇声集-", 04.21. Libra Records
24. iPhone for GAGLE - GAGLE (YouTube)
Produced by DJ Mitsu The Beats
exclusive, 07.02. (twitter)
25a. 24 BARS TO KILL feat. ANARCHY, RINO LATINA II, 漢, MACCHO - Ski Beatz (YouTube)
"24 HOUR KARATE SCHOOL JAPAN", 10.20. R-RATED RECORDS
25b. 24 Bars To Kill "IFK REMIX" feat. Hidaddy, Erone, Satussy, 遊戯 - Ski Beatz (YouTube)
exclusive, 11.05.
25c. 24 Bars To Kill "MYC REMIX" feat. RYUZO, DABO, SUIKEN, 般若 - Ski Beatz (YouTube)
exclusive, 11.06.
26. 東京哀歌-トウキョウエレジイ- - 空也MC (Youtube)
Produced by fanfan
27. 禁断の惑星 feat. 志人 - TABOO1 (YouTube)
Produced by DJ KENSEI
"LIFE STYLE MASTA", 10.20. Libra Records
28. Break Boy in the Dream feat. 七尾旅人 - 環ROY (YouTube)
"BREAK BOY", 03.17. POPGROUP RECORDINGS
29. DREM SKY - RAU DEF (YouTube)
Produced by PUNPEE
"ESCALATE", 09.10. FILE RECORDS
30. MOTHER feat. MMM, CHIYORI - おみゆきChannel (YouTube)
"おみゆきさん", 03.10. Mary Joy Recordings
31. MISSION POSSIBLE - OLIVE OIL×ILL-BOSSTINO×B.I.G.JOE (YouTube)
"MISSION POSSIBLE", 07.21. OILWORKS THA BLUE HERB RECORDINGS TRIUMPH RECORDS
32. WAKE UP feat. MARIA - QN from SIMI LAB (Amebreak)
"THE SHELL", 07.29. FILE RECORDS
http://labs.gree.jp/blog/2010/11/1953/
#!/usr/bin/ruby def fork_exec(*args) fork() { Kernel.exec(*args) } end fork_exec("/usr/bin/ruby", "-e", 'sleep 3; puts "3"') fork_exec("/usr/bin/ruby", "-e", 'sleep 1; puts "1"') fork_exec("/usr/bin/ruby", "-e", 'sleep 2; puts "2"') Process.waitall()
以下の記事を読んだ私は違和感を覚えた。
今時のソフトウェア技術者というものは未だにこんな判定処理を書いているというのか。やはり遅れているとしか言いようが無い。
闇プログラマー界隈ではその一歩後ろ右斜め奥をいっている。
うるう年の判定とはこうやるのだ。
def leap_year?(y) /^(?:[13579](?:[13579](?:[13579][26]|[26][048]?|[48][048]|0[48])|[48](?:[13579][26]|[48][048]?|[26][048]|0[48]?)|0(?:[13579][26]|[48][048]?|[26][048]|0[48])|[26](?:[048][048]?|[13579][26]|[26][048])?)|[26](?:[13579](?:[13579][26]|[26][048]?|[48][048]|0[48])|[26](?:[13579][26]|[48][048]?|[26][048]|0[48]?)|[48](?:[048][048]?|[13579][26]|[26][048])?|0(?:[13579][26]|[026][048]|[48][048]?)?)|[48](?:[13579](?:[13579][26]|[26][048]?|[48][048]|0[48])|[26](?:[13579][26]|[48][048]?|[26][048]|0[48]?)|[048](?:[048][048]?|[13579][26]|[26][048])?)?)$/ =~ y end
9999年までうるう年の判定ができる。
この程度で一端のソフトウェア技術者を名乗るなど、業界そのものが甘く見られたものだ。
まだまだ我々闇プログラマー集団が縁の下で支えてやらないとダメなのだと実感した。
なんか色々レスがついて嬉しいですねw
FizzBuzz問題って一般的に、3の倍数と5の倍数以外の場合は数字を表示しないと駄目だよ
それは知りませんでした。
後、def文の中で再帰してるけど、これ、馬鹿でかい数入れたらメモリ使い切らないか?
Phythonは詳しくないから、嘘言ってるかも知れないけど
他には、何も15で場合分けしなくても、改行しなければ3の倍数と5の倍数とそれ以外で済むから
剰余演算の回数が減る
この辺はいかにも興味が無い領域ですねw
メモリは多分使い切ると思いますが、使い切った場合とかめんどくさくて考えたくないです。
他には、何も15で場合分けしなくても、改行しなければ3の倍数と5の倍数とそれ以外で済むから
剰余演算の回数が減る
これも興味ないですね…。計算回数って、数学(数理モデル)を計算機に乗せたから出てきた問題で、実用上は重要だけど数学としてはどうでもいいなあって思っちゃいます。
JRuby上で動くRubyとJavaのログを同じファイルに保存したいときなど
JRuby界隈で何かいい方法ないかな~と探していたけど見つからないので
RubyのLoggerのインターフェースをcommons-loggingを使用して実装してみた
使用バージョンは以下
require 'logger' class CommonsLoggingLogger def initialize(name="ruby") @progname = nil @logger = org.apache.commons.logging.LogFactory.getLog(name) end def add(severity, message=nil, progname=@progname, &amp;block) if message.nil? and block_given? message = yield end case severity when Logger::DEBUG debug(progname){message} when Logger::INFO info(progname){message} when Logger::WARN warn(progname){message} when Logger::ERROR error(progname){message} else fatal(progname){message} end end def debug(arg0=nil, &amp;block) @logger.debug make_log(arg0, &amp;block) end def info(arg0=nil, &amp;block) @logger.info make_log(arg0, &amp;block) end def warn(arg0=nil, &amp;block) @logger.warn make_log(arg0, &amp;block) end def error(arg0=nil, &amp;block) @logger.error make_log(arg0, &amp;block) end def fatal(arg0=nil, &amp;block) @logger.fatal make_log(arg0, &amp;block) end def debug? @logger.isDebugEnabled end def info? @logger.isInfoEnabled end def warn? @logger.isWarnEnabled end def error? @logger.isErrorEnabled end def fatal? @logger.isFatalEnabled end def level if debug? Logger::DEBUG elsif info? Logger::INFO elsif warn? Logger::WARN elsif error? Logger::ERROR else Logger::FATAL end end def level=(lv) #do nothing end def sev_threshold level end def sev_threshold=(lv) #do nothing end def datetime_format nil end def datetime_format=(fm) #do nothing end attr_accessor :progname private def make_log(message_or_progname, &amp;block) if block_given? progname = message_or_progname || @progname message = yield else progname = @progname message = message_or_progname end progname_message(progname, message) end def progname_message(progname, message) progname.nil? ? message : "#{progname}: #{message}" end end
元ネタ http://phpspot.org/blog/archives/2009/12/phpjavascriptph_1.html
面白そうだと思ったので僕もやってみた。モジュールはPerl5.8系の標準モジュールのみ利用可という制限。
全部はキツイので関数処理関係の関数(http://php.benscom.com/manual/ja/ref.funchand.php)だけ実装してみた。
use strict; use warnings; =head2 call_user_func $ret = call_user_func($function,@param); $ret = call_user_func([$class,$method],@param); example1 sub plus { $_[0] + $_[1] } print call_user_func('plus',10,20); # 30 example2 package Foo; sub plus { $_[1] + $_[2] } package main; print call_user_func(['Foo','plus'],10,20); # 30 =cut sub call_user_func { my $proto = shift; if ( ref $proto eq 'ARRAY' ) { return $$proto[0]->${\$$proto[1]}(@_); } else { require Pod::Functions; if ( $Pod::Functions::Flavor{$proto} ) { return eval qq{$proto(\@_)}; } else { no strict 'refs'; return $proto->(@_); } } } =head2 call_user_func_array $ret = call_user_func_array($function,\@param); $ret = call_user_func_array([$class,$method],\@param); example sub plus { $_[0] + $_[1] } print call_user_func_array('plus',[10,20]); # 30 =cut sub call_user_func_array { return call_user_func(shift,@{+shift}); } =head2 create_function $code = create_function($args_str,$code_str); example $code = create_function('$c,$d=1','print $c+$d'); $code->(10); # 11 =cut sub create_function { my $args = shift; my $code = shift; my $default = 0; my @args = split /,/,$args; my $code_str = 'sub {'; for my $arg (@args) { if ( $arg =~ /^\s*(\$[a-zA-Z][\w]*)\s*(?:=\s*(.+))?\s*$/ ) { my $val = $1; my $def = $2; if ( defined $def ) { $default = 1; $code_str .= qq{my $val = \@_ ? shift : $def;\n}; } else { die 'parse error' if $default; $code_str .= qq{my $val = shift;\n}; } } } $code_str .= $code . '}'; my $sub = eval $code_str; die $@ if $@; return $sub; } =head2 forward_static_call $ret = forward_static_call($function,@param); $ret = forward_static_call([$class,$method],@param); =cut sub forward_static_call { call_user_func(@_); } =head2 forward_static_call_array $ret = forward_static_call_array($function,\@param); $ret = forward_static_call_array([$class,$method],\@param); =cut sub forward_static_call_array { call_user_func_array(@_); } =head2 func_get_arg $arg = func_get_arg($no) example sub foo { print func_get_arg(1) } foo(100,200); # 200 =cut sub func_get_arg { my $n = shift; package DB; @DB::args = (); () = caller(1); return defined $DB::args[$n] ? $DB::args[$n] : undef; } =head2 func_get_args @args = func_get_args() example sub foo { print join ':', func_get_args() } foo(11,22,33); # 11:22:33 =cut sub func_get_args { my $n = shift; package DB; @DB::args = (); () = caller(1); return @DB::args; } =head2 func_num_args $arg_count = func_num_args() example sub foo { print func_num_args() } foo(11,22,33); # 3 =cut sub func_num_args { my $n = shift; package DB; @DB::args = (); () = caller(1); return scalar @DB::args; } =head2 function_exists $bool = function_exists($func) example sub foo {} print function_exists('foo'); # 1 print function_exists('bar'); # 0 print function_exists('rand'); # 1 =cut sub function_exists { my $func = shift; return 1 if exists &$func; require Pod::Functions; return $Pod::Functions::Flavor{$func} ? 1 : 0; } =head2 get_defined_functions $funcs = get_defined_functions() =cut sub get_defined_functions { require Pod::Functions; return { internal => [ keys %Pod::Functions::Flavor ], user => [ grep { exists &$_ } keys %:: ], }; } =head2 register_shutdown_function register_shutdown_function($func,@param); register_shutdown_function([$class,$method],@param); =cut { my $REGISTER_SHUTDOWN_FUNCTION = []; sub register_shutdown_function { my $proto = shift; push @$REGISTER_SHUTDOWN_FUNCTION, [ do { if ( ref $proto eq 'ARRAY' ) { $$proto[0]->can($$proto[1]); } else { require Pod::Functions; if ( $Pod::Functions::Flavor{$proto} ) { sub { eval qq{$proto(\@_)} }; } else { no strict 'refs'; \&$proto; } } }, [@_] ] } END { $_->[0]->(@{$_->[1]}) for @$REGISTER_SHUTDOWN_FUNCTION; } }
思ったよりも難しかった。標準関数一覧を取る手段がなかったので標準モジュールを利用して標準関数の一覧を取得した。
あと文字列から標準関数を呼び出すスマートな手段が思いつかなかったのでeval便りに。
create_functionはかなりゴリ押し。myを勝手に付けたりデフォルト引数にも対応してたり細かい芸が光る(自分で言うな)
forward_static_callはぶっちゃけPerl的にcall_user_funcと殆ど処理が変わらないのでそのまま利用。
func_get_arg系は結構クリティカルだなー。@DB::argsをリアルに使ったの初めてだよ。
register_shutdown_functionはちょっとねー。ENDブロックを利用してるわけなんだけど当然mod_perlとかではうまく動かない。あとシグナルとか使った方が良いのかもしれない。
ヒマがあったら他の関数とかも実装してみたいかも。
非常に頭が悪いやり方ですが、一応動くので置いておきます
# -*- coding: utf-8 -*- require 'uri' require 'open-uri' require 'rubygems' require 'twitter' require 'json' LAST_YEAR = Time.now - 12*31*60*60*24 USERNAME = 'hogehoge' PASSWORD = '********' def following_me? other base = URI.parse('http://twitter.com/friendships/exists.json') base.query = "user_a=#{other}&amp;user_b=#{USERNAME}" times = 0 begin sleep 60 open(base).read == 'true' rescue if times < 2 times += 1 retry else true end end end def posted? hash begin sleep 60 last_post = @client.timeline_for(:user, :id=>hash[:user], :count=>1).first.created_at rescue Twitter::RESTError # protected ~A~K~A following ~A~L 0 ~A | ~P~H~A~Y~T~_~A~W~A~_~_ p "#{$!}" p "passing #{hash[:user]}" return true # ~D~P~F~_~P~F~J~\~A~Q~B~K rescue # twitter~A~L~P~A~A~B~K~Y~B対~V p "#{$!}" sleep 180 retry end p "last_post was #{last_post}" last_post > hash[:from] end @client = Twitter::Client.new(:login => USERNAME, :password => PASSWORD) (1..20).each do |pg| @client.my(:friends, :page=>pg).each do |friend| p "checking #{friend.screen_name}" unless ((following_me? friend.screen_name) &amp;&amp; posted?({:from=>LAST_YEAR, :user=>friend.screen_name})) p "removing #{friend.screen_name}" sleep 60 @client.friend(:remove, friend) end end end
突然だけど関数定義のキーワードは function がいいと思うんだよ。
ほれ、C とか Java とかああいう感じの言語はともかくとして、型がないとか型推論とか言ってる言語ってよく関数定義にキーワードあるじゃん。あれが無闇にバリエーションあって、俺はその中で function が一番まともだと思うんだよ。スペル長いんだけどさ。
バリエーションってのは、たとえば Scheme の lambda 。lambda ってお前。意味わかんないだろ。らむぶだ? なにそれ? って感じ。いや歴史的事情はわかる。λ計算知ってる。でもそれお前さー、普通まともな計算機科学の学科である種の講義に出てねーと知らんだろー? いやそりゃインターネッツじゃみんなわかるだろうけどさーググレカスで。でもリアルでそれ友達に通じる? Simple Kana Kanji って東北大だから通じたギャグだと思うよってそりゃλ計算じゃないけど。
Ruby の def はよくわかんないんだけど、あれ DEfine Function ってこと? class とか名詞なのになんでそこだけ動詞入ってんの? Common Lisp の defun も同類です。あと Python の def は definition の def らしいけど、お前の言語には定義するものが関数しかないのか。Perl (いい加減死んだか?) の sub もなんかなー。main はないけど sub ですかっていうか、サブルーチンっていう語が既にどっかしら過去形だよね。あと OCaml とかもう let と fun と function が混在していてわけがまあわかるけど嬉しくないよね。
究極的に終わってるのは Haskell で、バックスラッシュ。「バックスラッシュはλに似てる」とかどんな暴論だよ! 似てねえよ! vはハートですかそうですか! 大体日本人から見たらそりゃ限りなく YEN SIGN だっつの!
あー、なんか無駄に力入ったけど、function なんだよ結局。あのPHPと一緒っていうのが、あの数十年後の COBOL 化が俺の中で約束されている PHP と一緒っていうのがちょっとヤだけどさ。……え、何? プリプロセッサでも通しておけ? あ、はい仰るとおりです先生。
class Singleton( object ): _instances = dict() def __new__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = object.__new__(cls, *args, **kwargs) return cls._instances[cls] class A( Singleton ): def __init__( self, x ): self.x = x a = A(1) print id(a) print id(a.x) b = A(2) print id(b) print id(b.x)
最初のあなた。
foreach 男 in 気になる男 女.告白(男); new 返事待ちスレッド().start(); join(); continue if 返事.ごめん(); 二人 = new カップル(女,男); while !二人.別れた() 二人.ごにょごにょ(); end # 二人.dispose(); #コメントアウト while !女.気が済んだ() 女.アプローチ(男) end end
今のあなた。
new 女スレッド().start(); foreach 男 in 気になる男 女.告白(男); end 女スレッド extends スレッド def run while true 返事 = 返事キュー.get(); continue if 返事 == null; continue if 返事.ごめん(); 二人 = new カップル(女,男); while !二人.別れた() 二人.ごにょごにょ(); end 二人.dispose(); end end end
foreach 男 in 気になる男 女.告白(男); 返事待ちスレッド.start(); join(); continue if 返事.ごめん(); 二人 = new カップル(女,男); while !二人.別れた() 二人.ごにょごにょ(); end 二人.dispose(); end
できた。
def every_case(arr, *args) if(arr.length>1) arr.each do |elm| #~\尾1~A~A~A~B~K~A~A~[~^~A~Y~@~B履歴~A第2~U~U~A~]~L~A every_case(arr-[elm], args.push(elm)) args.pop #使~T~L~A履歴~H~N end else p args.push(arr).flatten end end sample = (1..8).to_a every_case(sample)
だいぶ前のことなので結構忘れてるけど。
買ったのはHHKBPro2。OSはWindowsVista
背面のスイッチをいじってキーを変える。
http://www.pfu.fujitsu.com/hhkeyboard/leaflet/hhkb_backview.html
| 左◇ | Fn |
| 右◇ | Win |
| Delete | Backspace |
左◇あたりにFnを割り振らないと右小指でしかFnを押せなくて面倒。
DeleteよりBackspaceの方が使用頻度が高いので変更。(Fn+DeleteでDeleteを押したことになります)
ノートPC(日本語版Windows)で英語配列版HHKBを使う時はUSJPを入れるのが限りなく正解に近い。
http://www.trinityworks.co.jp/software/USJPPro/index.php
デスクトップPCではドライバを英語キーボードに設定すればいい。
ノートPCでUSJPを入れていて、USJPにはおまけ機能でいくらかのキーの組み合わせを設定できるので
デスクトップPCでも同じにするためにとりあえず窓使いの憂鬱を入れた。
Vista対応版
http://slashdot.jp/~kazu2/journal/411746
設定はこんな感じにした。
include "104.mayu" key C-D = Delete key C-H = BackSpace key C-M = Enter key C-Space = $ToggleIME
どこかで見た設定をベースにいじったような気がしますがどこのがベースか忘れた。
自分は左◇キーをFnキーに設定して使っているので無変換キーをFnキーにするとちょうどよくなる。
include "104.mayu" mod control += CapsLock def subst *CapsLock = *LControl key S-Pause = CapsLock def key 無変換 = 0x7b mod mod0 = 無変換 key *S-M0-K = *S-Home key *S-M0-Comma = *S-End key *S-M0-Apostrophe = Right key *S-M0-Semicolon = Left key *S-M0-LeftSquareBracket = Up key *S-M0-Solidus = Down key *S-M0-L = *S-PageUp key *S-M0-FullStop = *S-PageDown key M0-R = F5 key C-D = Delete key C-H = BackSpace key C-M = Enter key *S-M0-_1 = F1 key *S-M0-_2 = F2 key *S-M0-_3 = F3 key *S-M0-_4 = F4 key *S-M0-_5 = F5 key *S-M0-_6 = F6 key *S-M0-_7 = F7 key *S-M0-_8 = F8 key *S-M0-_9 = F9 key *S-M0-_0 = F10 key *S-M0-HyphenMinus = F11 key *S-M0-EqualsSign = F12 def key JisBackslash = 0x7d key JisBackslash = Backslash key S-JisBackslash = $VERTICAL_LINE key C-Space = $ToggleIME
HHKBを繋いだ時は上の設定、普通のキーボードを繋いだ時は下の設定に切り替えてやってます。
USBメモリを挿したい時もここ。
重量が結構ある。
今年の情報オリンピック予選のこの問題、
冬の寒いある日,JOI太郎君は広場にはった薄氷を割って遊ぶことにした.広場は長方形で,東西方向に m 個,南北方向に n 個,つまり, m × n の区画に区切られている.また,薄氷が有る区画と無い区画がある. JOI太郎君は,次のルールにしたがって,薄氷を割りながら区画を移動することにした.
東西南北のいずれかの方向に隣接し, まだ割られていない薄氷のある区画に移動できる.
移動した先の区画の薄氷をかならず割る.
JOI太郎君が薄氷を割りながら移動できる区画数の最大値を求めるプログラムを作成せよ.ただし, 1 ≦ m ≦ 90,1 ≦ n ≦ 90 である.与えられる入力データでは,移動方法は20万通りを超えない.
入力はn+2行ある. 1 行目には整数 m が書かれている. 2 行目には整数 n が書かれている. 3 行目から n+2 行目までの各行には、0 もしくは 1 が, 空白で区切られて m 個書かれており,各区画に薄氷があるか否かを表している.北から i 番目,西からj番目の区画を (i,j) と記述することにすると (1 ≦ i ≦ n, 1 ≦ j ≦ m),第 i+2 行目の j 番目の値は,区画 (i,j) に薄氷が存在する場合は 1 となり,区画 (i,j) に薄氷が存在しない場合は 0 となる.
のいい最適化の方法が思いつかない。
一応プログラムは書けたけど90*90の時にまともに動く気がしない。
単なる深さ優先検索。
array = [ [1, 1, 1, 0, 1], [1, 1, 0, 0, 0], [1, 0, 0, 0, 1] ] result = [] def get(x, y): if (0 <= x < len(array)) and (0 <= y < len(array[0])): return array[x][y] else: return -1 def func(x, y, route): route = route[:] # print x, y, get(x, y) if get(x, y) == 0: result.append(route) return if (x, y) in route: return route.append((x, y)) if get(x - 1, y ) >= 0: func(x - 1, y , route) if get(x , y + 1) >= 0: func(x , y + 1, route) if get(x + 1, y ) >= 0: func(x + 1, y , route) if get(x , y - 1) >= 0: func(x , y - 1, route) func(0, 0, []) for i in result: print i
YourFileHostのCAPTCHA画像をなんとかするの続きの続き。
まぁ、なんというか、一応できたので張ってみる。微妙な出来栄えだけど。
decaptcha.rbと同じディレクトリに置いて適当に動かしてみてください。
どっかにいいRSSがないかなぁ。。。
しかし、CAPTCHA画像を相手にしてた時の方が楽しかったなぁ。。。
あと、添削とかいろいろ歓迎です。
なぜか&が&amp;に置き換えられてるみたいなので、適当に&に読み替えてください。
#!/usr/local/bin/ruby $LOAD_PATH << File::dirname(File::expand_path($0)) require 'rubygems' require 'rss' require 'mechanize' require 'decaptcha' # require 'ruby-debug' # require 'pp' DESTDIR = 'files' TMPDIR = '/tmp' class Downloader COOKIE_URI = 'http://www.yourfilehost.com/media.php?cat=video&file=%s' DOWNLOAD_URI = 'http://www.yourfilehost.com/downloadlink.php?cat=video&file=%s&adult=1' VERBOSE = false DEBUG = false def self.fetch(file, destdir) puts "Fetching file: #{file}" destfile = destdir + '/' + file if test(?e, destfile) then puts ' - Already exists. skip' puts return nil end # # Initialize Mech # mech = WWW::Mechanize.new mech.user_agent_alias = 'Windows IE 6' mech.max_history = 3 mech.open_timeout = 15 mech.read_timeout = 3 # # get cookie # uri = COOKIE_URI % file page = mech.get(uri) # # Challenge against CAPTCHA # uri = DOWNLOAD_URI % file page = mech.get(uri) for i in 0...3 captcha_path = (page/'img[@height="38"]').attr(:src) captcha_uri = 'http://www.yourfilehost.com/%s' % captcha_path gif = mech.get_file(captcha_uri) open("#{TMPDIR}/captcha.gif", 'w') {|fd| fd.write(gif) } mech.back code = DeCAPTCHA.decode("#{TMPDIR}/captcha.gif") File.unlink("#{TMPDIR}/captcha.gif") if code.nil? then puts ' - CAPTCHA decode failed. retry' if VERBOSE next end form = mech.page.forms.first form.verify = code page = mech.submit(form) break unless page.links.empty? end if page.links.empty? then puts ' - Failed 3 times. Try another one.' puts return nil end # # download # puts " - Downloading: #{page.links.first.href}" if DEBUG retry_count = 0 video = nil begin video = page.links.first.click rescue Timeout::Error => evar retry_count += 1 if retry_count < 5 then puts " * Timedout, retry" if VERBOSE retry end raise evar end unless video.instance_of?(WWW::Mechanize::File) then if DEBUG then puts " - Something wrong while downloading. skip." puts end return nil end video.save(destfile) return destfile end end # # main # [DESTDIR, TMPDIR].each do |dir| if !test(?d, dir) or !test(?w, dir) then puts "#{dir}: Directory not exists or cannot write." exit end end files = [] # collect URIs from RSS RSS_URI = [ ['http://www.yourfilehost.com/make-rss.php?range=day&af=off', lambda {|rss| rss.items.map {|x| x.link }}, ], ] RSS_URI.each do |uri, preprocessor| rss = RSS::Parser.parse(uri) uris = preprocessor.call(rss) #=> Array of URI of YourFileHost uris.map {|x| /file=([^&]*)/.match(x).to_a[1] }.each do |file| # next if file !~ /\.wmv/ # uncomment it if you need only wmv files << file end end files.uniq! # download files.each do |file| begin Downloader.fetch(file, DESTDIR) rescue SystemCallError, Timeout::Error => evar puts " - error (#{evar.to_s}). skip." puts end end __END__
せっかく書いたから匿名でのせてみるよ
使い方は
必要なものを gem で取ってくるにはこうすればいいよ
長すぎてelispが消えたから続きがあるよ
@echo off setlocal set WD=%~dp0 cd /d %WD% ruby get_movies.rb ruby get_images.rb ruby create_m3u.rb
user: ユーザID password: パスワード ids_file: ids.txt done_file: ids_done.txt movies_dir: movies log4r_config: pre_config: global: INFO loggers: - name: app type: Log4r::Logger level: INFO outputters: - STDOUT - FILE outputters: - name: STDOUT type: Log4r::StdoutOutputter formatter: type: Log4r::PatternFormatter pattern: "%d [%l] %C - %M" date_pattern: "%H:%M:%S" - name: FILE type: Log4r::FileOutputter filename: "#{LOGDIR}/sangels.log" formatter: type: Log4r::PatternFormatter pattern: "%d [%l] %C - %M" date_pattern: "%Y-%m-%d %H:%M:%S"
require 'fileutils' require 'logger' require 'mechanize' BASEDIR = File.dirname($0) require "#{BASEDIR}/util" require "#{BASEDIR}/sangels" $config = load_config(BASEDIR) prepare_logger(BASEDIR) $log = new_logger("get_movies") WWW::Mechanize.log = new_logger("mechanize") WGet.log = $log class IDFile def initialize(file) @file = file unless File.exist?(@file) Fileutils.touch(@file) end end def ids(contains_comment = nil) File.open(@file) {|io| io.to_a.map {|x| x.chomp }.select {|x| if x.empty? nil elsif contains_comment true else not /^\s*\#/ =~ x end } } end def add(id) ids = ids(true) unless ids.any? {|x| x == id} write(ids + [id]) end end def delete(id) ids = ids(true) if ids.any? {|x| x == id} write(ids - [id]) end end def write(ids) File.open(@file, "w") {|io| ids.each {|x| io.puts x} } end end $log.info("BEGIN #{$0} ================") exit_code = 0 begin ids_file = IDFile.new($config.ids_file) done_file = IDFile.new($config.done_file) movies_dir = $config.movies_dir wget = WGet.new sangels = SAngels.new sangels.login($config.user, $config.password) ids_file.ids.each {|id| begin movies = sangels.movies(id) rescue SAngels::Movies::InvalidMoviesError $log.warn("invalid movie id: #{id}") next end dir = File.expand_path(id, movies_dir) movies.each {|link| wget.retrieve(link.href, dir) } expected = movies.movie_links.map{|x| File.basename(x.href)} actual = Dir.glob("#{dir}/*").map {|x| File.basename(x)} if (expected - actual).empty? done_file.add(id) ids_file.delete(id) end } rescue => e $log.error(e) exit_code = 1 end $log.info("END #{$0} (#{exit_code}) ================") exit exit_code
require 'fileutils' require 'logger' require 'mechanize' require 'ostruct' BASEDIR = File.dirname($0) require "#{BASEDIR}/util" require "#{BASEDIR}/sangels" $config = load_config(BASEDIR) prepare_logger(BASEDIR) $log = new_logger("get_images") WWW::Mechanize.log = new_logger("mechanize") WGet.log = $log $log.info("BEGIN #{$0} ================") exit_code = 0 begin movies_dir = $config.movies_dir sangels = SAngels.new sangels.login($config.user, $config.password) thumbnails = sangels.thumbnails Dir.glob("#{movies_dir}/*").each {|dir| next unless File.directory? dir id = File.basename(dir) url = thumbnails.url(id) unless url $log.warn("#{id} is not found") next end path = File.expand_path("00_thumbnail#{File.extname(url)}", dir) next if File.exist? path $log.info("retrieving #{url}") thumbnail = thumbnails.get_file(id) File.open(path, "wb") {|io| io.write(thumbnail)} } rescue => e $log.error(e) exit_code = 1 end $log.info("END #{$0} (#{exit_code}) ================") exit exit_code
BASEDIR = File.dirname($0) require "#{BASEDIR}/util" $config = load_config(BASEDIR) movies_dir = $config.movies_dir Dir.glob("#{movies_dir}/*") {|dir| next unless File.directory? dir name = File.basename(dir) files = Dir.glob("#{dir}/*.wmv").sort File.open("#{movies_dir}/#{name}.m3u", "w") {|io| files.each {|file| io.puts "#{name}/#{File.basename(file)}" } } File.open("#{dir}/00_movies.m3u", "w") {|io| files.each {|file| io.puts "#{File.basename(file)}" } } }
require 'mechanize' require 'hpricot' BASEDIR = File.dirname($0) require "#{BASEDIR}/util" class SAngels HOST = "real2.s-angels.com" LOGIN_URL = "http://#{HOST}/member/" INFO_URL = "http://#{HOST}/teigaku/item.php" THUMBNAILS_URL = "http://#{HOST}/teigaku/" THUMBNAIL_URL = "http://#{HOST}/images/default/thumb/" def initialize() @agent = WWW::Mechanize.new end def login(user, password) login_form = @agent.get(LOGIN_URL).forms.find {|form| form.fields.any? {|field| field.name == "frmLoginid"} } login_form.frmLoginid = user login_form.frmPw = password @agent.submit(login_form) end def movies(id, no_validate = nil) Movies.new(@agent, id, !no_validate) end def thumbnails Thumbnails.new(@agent) end class Thumbnails def initialize(agent) @agent = agent doc = Hpricot(@agent.get_file(THUMBNAILS_URL)) elems = doc.search("div[@class=realthum]/a") @links = Hash( elems.map {|elem| href = elem["href"] id = $1 if /ID=(.+)/ =~ href url = elem.search("img")[0]["src"] [id, url] }) end def get_file(id) @agent.get_file(url(id)) end def url(id) @links[id] end def exist?(id) url(id) end end class Movies class InvalidMoviesError < StandardError end def initialize(agent, id, no_validate) @agent = agent @id = id if !no_validate && !valid? raise InvalidMoviesError end end def info_page_url "#{INFO_URL}?ID=#{@id}" end def info_page @agent.get(info_page_url) end def movies_page @agent.click(info_page.links.find {|link| /P=10/ =~ link.href}) end def movie_links movies_page.links.select {|link| /wmv$/ =~ link.href }.sort {|a, b| File.basename(a.href) <=> File.basename(b.href) } end def valid? info_page.uri.to_s == info_page_url end def each(&block) orig_links = movie_links orig_links.each {|orig_link| link = movie_links.find {|l| File.basename(l.href) == File.basename(orig_link.href)} block.call(link) } end end end
require 'log4r' require 'log4r/yamlconfigurator' require 'singleton' require 'fileutils' require 'ostruct' def Hash(a) Hash[*a.flatten] end def load_config(basedir) OpenStruct.new(File.open("#{basedir}/config.yaml") {|io| YAML.load(io)}) end def new_logger(name) Log4r::Logger.new("app::#{name}") end def prepare_logger(basedir, logdir = nil) logdir ||= basedir Log4r::YamlConfigurator["LOGDIR"] = logdir Log4r::YamlConfigurator.load_yaml_file("#{basedir}/config.yaml") end class NullObject include Singleton def method_missing(message, *arg) NullObject.singleton end end class WGet class << self attr_accessor :log def initialize super @log = NullObject.singleton end end def log self.class.log end def retrieve(url, dir) FileUtils.mkdir_p(dir) file = File.expand_path(File.basename(url), dir) if File.exist?(file) log.info("already retrieved #{url}") return true end tmp = "#{file}.part" log.info("retrieving #{url}") ret = system("wget", "-c", "-O", tmp, url) if ret log.info("retrieving succeeded #{url}") File.rename(tmp, file) else if $? == 0x020000 # Ctrl-C exit($?) else log.error("retrieving failure #{url} (#{$?})") end end return ret end end
YourFileHostのCAPTCHA画像をなんとかするの続き。
その後、適当にいじったら、手元環境で1枚あたり25秒くらい→だいたい2.5秒くらいで判別できるようになった。このくらいなら使えるかな。
速度向上に一番効いたのは、Token#importで画像を比較しているところの修正。他は細かい手直し。
使い方は前のやつと変わってません。
あと、テストに100枚くらいCAPTCHA画像食わせてみたけど、とりあえず全部正しく判定できた。
カレントディレクトリ以下にある*.gifなCAPTCHA画像ファイルを適当に判別するスクリプト。動作確認用にどうぞ。
後述のdecaptcha.rbと同じディレクトリに適当に置いてchmod +xしてね。
#!/usr/local/bin/ruby $LOAD_PATH << File::dirname(File::expand_path($0)) require 'decaptcha' STDOUT.sync = true Dir.glob('*.gif').sort.each do |file| correct = File::basename(file, '.*') puts "Processing file: #{file}" start_time = Time.now ret = DeCAPTCHA.decode(file) elapsed = Time.now - start_time puts " Result: #{ret} (=> #{(correct == ret) ? "Ok" : "Fail"})" puts " Elapsed time: #{elapsed}" puts end
#!/usr/local/bin/ruby require 'rubygems' require 'gd2' require 'pp' # #= CAPTCHA画像解析モジュール # CAPTCHA画像ファイルを食わすとあら不思議、Stringが出てくるよ。 # YourFileHostのやつに対応。 # #== Usage # decoded_str = DeCAPTCHA.decode("some_captcha_image.gif") #=> String # 失敗したらnilが返る。 # module DeCAPTCHA DEBUG = false #=== CAPTCHA画像デコード # file:: 画像ファイル名のパス # method:: 未指定でよい。男は細かい事を気にするな。 # returns:: CAPTCHA画像解析結果(String) or nil (デコード失敗時) def self.decode(file, method = DeCAPTCHA::Site::YourFileHost) return method.new(file).decode end #= CAPTCHA画像デコード用クラス # このクラスのサブクラスはimport, tokenize, stream_parseメソッドの # 実装を含む必要がある。 class Site def initialize(file = nil) @pix = nil self.import(file) unless file.nil? end def decode return stream_parse(tokenize()) end end #= YourFileHostのCAPTCHA画像を解析するクラス class Site::YourFileHost < Site def import(file) @pix = PixelMatrix.new.import(file) return self end # importしたイメージ(PixelMatrix)から、文字と思わしきパターンを # 抽出して上下マージンを切り取ってArrayにして返す。 # returns:: Array of PixelMatrix def tokenize ret = [] state = :initial for x in 0...@pix.width case state when :initial if !@pix.vline_blank?(x) then state = :tokenize pixel = PixelMatrix.new(0, 0, true) ret << pixel redo end when :tokenize if @pix.vline_blank?(x) then state = :initial next end x0 = pixel.width for y in 0...@pix.height pixel[x0, y] = @pix[x, y] end else raise 'NOTREACHED' end end ret.map! {|token| Token.new.import(token.cutoff_vmargin!) } end # PixelMatrixのArrayを受け取り、数字を判別。 # tokens:: Array of PixelMatrix # returns:: String (判別結果) def stream_parse(tokens) rs = tokens.map {|x| x.guess.to_s }.join('') if rs.length != 4 then rs = nil if DEBUG then puts '- guess failed. dumping guess result of each token:' tokens.each_index do |i| print "##{i}:#{tokens[i].guess} " pp tokens[i].candidate end puts end end return rs end class Token @@digits = nil attr_accessor :candidate def initialize if @@digits.nil? then # 文字画像サンプルを作っておく @@digits = DIGITS_ASSOC.map {|digit| PixelMatrix.new(0, 0, true).import_array(digit) } end @candidate = Hash.new end # PixelMatrixを受け取り、文字画像サンプルと比較して # 一致率を計算しておく。 # pixel:: PixelMatrix # returns:: self def import(pixel) @@digits.each_index do |i| digit = @@digits[i] if (digit.width - pixel.width).abs > 4 or (digit.height - pixel.height).abs > 4 then @candidate[i] = -1 # サイズが違いすぎな場合、一致させない next end correct_bits = 0 enlarged_width = [digit.width, pixel.width ].max enlarged_height = [digit.height, pixel.height].max for y in 0...enlarged_height dy = (y.to_f / digit.height * enlarged_height).to_i py = (y.to_f / pixel.height * enlarged_height).to_i for x in 0...enlarged_width dx = (x.to_f / digit.width * enlarged_width).to_i px = (x.to_f / pixel.width * enlarged_width).to_i correct_bits += 1 if digit[dx, dy] == pixel[px, py] end end @candidate[i] = correct_bits * 100 / (enlarged_width * enlarged_height) end return self end # importのときの比較結果をもとに文字を推測 # returns:: Fixnum or nil(失敗時) def guess digit, ratio = @candidate.sort {|a, b| a.last <=> b.last}.last digit = nil if ratio < 0 or ratio < 65 return digit end end end #= 画素マトリックスクラス # 画像ファイルを食わせると、各ピクセル(画素)を2値(black(1) or white(0))に # 変換して、内部で保持する。 # 以後、Matrixクラスのような感じで個々の画素にアクセスできる。 class PixelMatrix BLACK = 1 WHITE = 0 attr_accessor :width attr_accessor :height # width:: 幅 # height:: 高さ # is_flexible:: 自動的に伸張するか def initialize(width = 0, height = 0, is_flexible = false) @matrix = Hash.new {|hash, key| hash[key] = Hash.new(WHITE)} @width, @height, @flexible = width, height, is_flexible end # file:: 画像ファイル名のパス # brightness_threshold:: 画素を黒とみなす閾値 (0 - 255, default: 0x40) # returns:: self (DeCAPTCHA::PixelMatrix) def import(file, brightness_threshold = 0x40) gd = GD2::Image.import(file) @width, @height = gd.width, gd.height self.each_with_axis do |x, y| color = gd[x, y] greyscale = (color.red + color.green + color.blue) / 3 self[x, y] = (greyscale > brightness_threshold) ? WHITE : BLACK end return self end def import_array(array) array.each_with_index do |str, y| str.split('').each_with_index do |c, x| self[x, y] = c.to_i end end return self end # PixelMatrixを画像ファイルとしてexport # file:: 新たに作る画像ファイル名のパス def export(file) gd = GD2::Image::IndexedColor.new(@width, @height) gd.palette << GD2::Color::WHITE gd.palette << GD2::Color::BLACK self.each_with_axis do |x, y| gd[x, y] = { WHITE => GD2::Color::WHITE, BLACK => GD2::Color::BLACK, }[self[x, y]] end gd.export(file) return self end # 指定された位置の画素を返す。 # returns:: PixelMatrix::BLACK(1) or WHITE(0) def [](x, y) if !@flexible and !in_range?(x, y) then raise RangeError end return WHITE if !@matrix.has_key?(y) # XXX: for optimize return @matrix[y][x] end # 画素に値を設定。 # returns:: PixelMatrix::BLACK(1) or WHITE(0) def []=(x, y, val) unless in_range?(x, y) then raise RangeError unless @flexible @width = (x >= @width) ? x + 1 : @width @height = (y >= @height) ? y + 1 : @height end @matrix[y][x] = val end def in_range?(x, y) ((0...@width) === x and (0...@height) === y) end # 指定された軸をもとに画素を走査し、Arrayに変換。 # 例えば、to_a(:vertical, 10) とすると、x == 10 な列を取り出して # Arrayにして返す。 # # axis:: 軸を指定 (:vertical または :horizontal) # pos:: 位置を指定。_axis_で指定した軸と直交する軸における位置を指定。 def to_a(axis, pos) {:vertical => lambda { (0...@height).map {|y| self[pos, y]} }, :horizontal => lambda { (0...@width).map {|x| self[x, pos]} }, }[axis].call end # returns:: Array def hline(y) self.to_a(:horizontal, y) end # returns:: Array def vline(x) self.to_a(:vertical, x) end # X軸方向に画素を走査。 # y:: どの位置で走査するか # returns:: 指定された軸の上にドットが存在: false, 無い: true def hline_blank?(y) return true if @matrix.has_key?(y) == false # XXX: for optimize for x in 0...@width return false if self[x, y] == BLACK end return true end # Y軸方向に画素を走査。 # x:: どの位置で走査するか # returns:: 指定された軸の上にドットが存在: false, 無い: true def vline_blank?(x) for y in 0...@height return false if self[x, y] == BLACK end return true end # 上下のマージンを削除した新しいPixelMatrixを返す。 # returns:: PixelMatrix def cutoff_vmargin pixel = PixelMatrix.new(0, 0, true) head = 0 tail = self.height - 1 head.upto(tail) do |y| if !self.hline_blank?(y) then head = y break end end tail.downto(head) do |y| if !self.hline_blank?(y) then tail = y break end end head.upto(tail) do |y| 0.upto(self.width - 1) do |x| pixel[x, y - head] = self[x, y] end end return pixel end # 自身の上下のマージンを削除する。cutoff_vmarginの破壊版。 # 速度稼ぎのために直接@matrixを触ったり、すこしずるをしている。 # 効率は、ほんの少しだけ速くなったような誤差の範囲のような感じ。 # returns:: self (PixelMatrix) def cutoff_vmargin! head = 0 tail = self.height - 1 head.upto(tail) do |y| if !self.hline_blank?(y) then head = y break end end tail.downto(head) do |y| if !self.hline_blank?(y) then tail = y break end @matrix.delete(y) if @matrix.has_key?(y) # XXX end if head > 0 then head.upto(tail) do |y| next if !@matrix.has_key?(y) # XXX @matrix[y - head] = @matrix.delete(y) # XXX end end self.height = tail - head + 1 return self end def each_with_axis for x in 0...@width for y in 0...@height yield(x, y) end end end end end class DeCAPTCHA::Site::YourFileHost::Token DIGITS_ASSOC = [ # 0 ["00000000011111110000000000", "00000001111111111110000000", "00000011111000001111000000", "00001111111100010011110000", "00011111100000110000111000", "00111111000000100001111000", "00111111110001000001111100", "01111111000010000011111110", "01111100000110000111111110", "01111111000100000111111110", "11111100001000001111111111", "11100000001000011111111111", "11111000010000011111111111", "11111000110000111111111111", "10000000100001111111111111", "01100001000001111111111110", "01100010000011111111111110", "01100010000111111111111110", "00111100000111111111111100", "00011100001111111111111000", "00001100011111111111111000", "00001111111111111111100000", "00000011111111111111000000", "00000000111111111100000000"], # 1 ["00001", "00111", "11111", "11111", "10001", "00001", "00001", "00001", "00001", "00001", "00001", "00001", "00001", "00001", "00001", "00001", "00001", "00001", "00001", "00001"], # 2 ["0000011111111110000000", "0001111111111111000000", "0011110000000010000000", "0110000000000100000000", "1100000000001100011110", "1000000000001000001111", "1000000000010000000111", "1000000000110001111111", "1000000000100001111111", "1000000001000000011111", "0100000011000001111111", "0011000010000111111110", "0011000110000001111110", "0000001100000111111100", "0000001000011111110000", "0000011000000111100000", "0000110000000000000000", "0000111111111111111111", "0001111111111111111110", "0011111111111111111100", "0011111111111111111100", "0111111111111111111000", "0111111111111111110000"], # 3 ["000000011111111110000000", "000001111111111111100000", "000011100000011111111000", "000111000000111111111000", "000110000001111111111100", "000100000001111111111100", "000100000011111111111100", "000110000111111111111000", "000010000111111111111000", "000000001111111111100000", "000000011111111111000000", "000000011111111110000000", "000000000000001100000000", "000000000000011100011100", "000000000000111000111110", "000000000000110000001110", "000000000001110001111111", "110000000011100011111111", "111000000111000000111110", "011100000110001111111100", "001111001110000111111000", "000011111100000011100000", "000000011000111000000000"], # 4 ["0000000000011", "0000000000011", "0000000000111", "0000000001111", "0000000001111", "0000000011111", "0000000111111", "0000000110111", "0000001100111", "0000011100111", "0000011000111", "0000110000111", "0001110000111", "0001100000111", "0011000000111", "0111000000111", "0111111111111", "1111111111111", "0000000000111", "0000000000111", "0000000000111", "0000000000111", "0000000000111"], # 5 ["000000001111111111111110", "000000011111111111111100", "000000111111111111111100", "000000111111111111111000", "000001111111111111110000", "000011100000000000000000", "000011011111111110000000", "000111111111111111000000", "001111100000000111000000", "001110000000000110001100", "000000000000000100011110", "000000000000001100000110", "000000000000011000011111", "000000000000011001111111", "000000000000110000011111", "000000000001100000111111", "110000000001100111111110", "011000000011000001111110", "011100000110000011111100", "001111000110011111111000", "000111111100001111110000", "000001111100000011000000", "000000001000011000000000"], # 6 ["000000000000000110000000", "000000010001111111111000", "000001110000011100111100", "000011100000011000001000", "000111000011111000000000", "001111000001110000000000", "001110000000111100000000", "011110001111111111100000", "011100000111000011110000", "011000000010000001111000", "011000011110000011111100", "010000111100000111111110", "100000001000000111111110", "100001111000001111111111", "000011110000011111111111", "000000100000011111111111", "000011100000111111111111", "001111000001111111111110", "000010000001111111111110", "001110000011111111111100", "000111000111111111111000", "000011111111111111110000", "000000111111111111000000", "000000000111111000000000"], # 7 ["0011111111111110001111", "0011111111111100000011", "0111111111111000000110", "1111111111111000111100", "1111111111110000001100", "0000000000000000011000", "0000000000000011111000", "0000000011000000110000", "0000001110000011100000", "0000011110001111100000", "0000111100000011000000", "0001111000000110000000", "0001111000111110000000", "0011110000001100000000", "0011110000001000000000", "0011100011111000000000", "0011000001110000000000", "0001000000110000000000", "0000000111100000000000", "0000000111000000000000", "0000000011000000000000", "0000001110000000000000", "0000001100000000000000"], # 8 ["0000000111111111110000000", "0000011111111111111100000", "0000001111100000011110000", "0000000001110000000111000", "0011000011111000000011100", "0011100001111100000011100", "0011110000001110000011100", "0001111000011111000111000", "0000111100000111101110000", "0000011110000001111100000", "0000001111000011111110000", "0000111111110000011111000", "0011110011111000001111100", "0111100001111100011111100", "0111000000111110000111110", "1111000000011111000000111", "1111000000001111100011111", "1111000000000011111000010", "0111100000000001111100000", "0011110000000000111110000", "0001111110000001111110000", "0000011111111111111000000", "0000000011111110000000000"], # 9 ["000000111111111110000000", "000001111111111111100000", "000111111111111001111000", "001111111111110000010000", "011111111111110000010000", "011111111111100000100000", "011111111111000001100000", "111111111110000001000111", "111111111110000010000001", "111111111100000110000111", "011111111000000100011111", "011111111000001000001111", "001111110000011000011111", "001111100000010001111110", "000111110000100000011110", "000001111111100000111110", "000000011111000111111100", "000000000010000001111100", "000000000110000001111000", "000100001100011111110000", "000111001100001111100000", "000011111000001111000000", "000000010000110000000000"], ] end __END__
そんなわけで、みんな大好きなYourFileHostだけども、最近みてみたら、なんかCAPTCHA認証がついているわけじゃないですか。
でもこれってさーCAPTCHAといっても見るからに危ういというか、見れば見るほど簡単に破れてしまいそうな気がして、どうにもむずがゆいアンニュイな気分になってしまうわけです。
そんで、このたび適当にいじってみたところ、それなりに推測できるコードが書けたので、ここに張ってみますね。
やってることは単純で、こんな感じ
使い方は、こんな感じで
require 'decaptcha.rb' captcha_string = DeCAPTCHA.decode(filename) if !captcha_string.nil? then # 判別成功時の処理 else # 判別失敗時の処理。失敗することもあるのでよしなに。 end
あ、Ruby/GD2入れといてね。sudo gem install gd2とかで入ります。多分。
判別率はそこそこ良い感じになったんだけども、富豪プログラミングがたたってか、いかんせん遅いです。
手元だと1枚判別するのに20秒くらいかかることもある。
そんなわけで誰かチューニングしてくれるとうれしい。
実行速度を上げた改良版はこっちにうpしました。以下のコードは参考程度に。
#!/usr/local/bin/ruby require 'rubygems' require 'gd2' require 'pp' # #= CAPTCHA画像解析モジュール # CAPTCHA画像ファイルを食わすとあら不思議、Stringが出てくるよ。 # YourFileHostのやつに対応。 # #== Usage # decoded_str = DeCAPTCHA.decode("some_captcha_image.gif") #=> String # 失敗したらnilが返る。 # module DeCAPTCHA DEBUG = false #=== CAPTCHA画像デコード # file:: 画像ファイル名のパス # method:: 未指定でよい。男は細かい事を気にするな。 # returns:: CAPTCHA画像解析結果(String) or nil (デコード失敗時) def self.decode(file, method = DeCAPTCHA::Site::YourFileHost) return method.new(file).decode end #= CAPTCHA画像デコード用クラス # このクラスのサブクラスはimport, tokenize, stream_parseメソッドの # 実装を含む必要がある。 class Site def initialize(file = nil) @pix = nil self.import(file) unless file.nil? end def decode return stream_parse(tokenize()) end end #= YourFileHostのCAPTCHA画像を解析するクラス class Site::YourFileHost < Site def import(file) @pix = PixelMatrix.new.import(file) return self end # importしたイメージ(PixelMatrix)から、文字と思わしきパターンを # 抽出して上下マージンを切り取ってArrayにして返す。 # returns:: Array of PixelMatrix def tokenize getter, tokenizer = lambda {|queue| [ lambda { return queue }, lambda {|x, pixel| if pixel.nil? then x, pixel = tokenizer.call(x, PixelMatrix.new(0, 0, true)) queue << pixel return x end if !@pix.in_range?(x, 0) or @pix.vline_blank?(x) then return [x, pixel] end x0 = pixel.width @pix.vline(x).inject(0) do |y, color| pixel[x0, y] = color y + 1 end return tokenizer.call(x + 1, pixel) } ] }.call([]) x = 0 while (x < @pix.width) x = @pix.vline_blank?(x) ? x + 1 : tokenizer.call(x, nil) end getter.call.map do |token| # Token.new.import(token.cutoff_vmargin.shrink) Token.new.import(token.cutoff_vmargin) end end # PixelMatrixのArrayを受け取り、数字を判別。 # tokens:: Array of PixelMatrix # returns:: String (判別結果) def stream_parse(tokens) r = [] tokens.inject(nil) do |prev, cur| r << cur.guess end rs = r.map {|x| x.to_s}.join('') if rs.length != 4 then if DEBUG then puts '- guess failed. dumping guess result of each token:' tokens.each_index do |i| print "##{i}: " pp tokens[i].candidate end puts end return nil end return rs end class Token @@digits = nil attr_accessor :candidate def initialize if @@digits.nil? then # 文字画像サンプルを作っておく @@digits = DIGITS_ASSOC.map {|assoc| PixelMatrix.new(0, 0, true).import_assoc(assoc) } end @candidate = Hash.new end # PixelMatrixを受け取り、文字画像サンプルと比較して # 一致率を計算しておく。 # pixel:: PixelMatrix # returns:: self def import(pixel) gcd = lambda {|a, b| a, b = [b, a] if a < b return a if b == 0 r = a % b return gcd.call(b, r) } lcm = lambda {|a, b| a * b / gcd.call(a, b) } mul_to_lcm = lambda {|a, b| g = gcd.call(a, b) [b / g, a / g] } 0.upto(@@digits.size - 1) do |i| if (@@digits[i].width - pixel.width).abs > 4 or (@@digits[i].height - pixel.height).abs > 4 then @candidate[i] = -1 # 比較対象とサイズが違いすぎ next end mul_ax, mul_bx = mul_to_lcm.call(@@digits[i].width, pixel.width) mul_ay, mul_by = mul_to_lcm.call(@@digits[i].height, pixel.height) enlarged_width = @@digits[i].width * mul_ax enlarged_height = @@digits[i].height * mul_ay # 文字画像サンプルと比較対象画像のサイズをあわせる # 幅・高さをそれぞれ適当に整数倍して、最小公倍数に合わせて比較 # (めんどうだから) correct_bits = 0 (0...enlarged_width).each do |x| (0...enlarged_height).each do |y| if @@digits[i][x/mul_ax, y/mul_ay] == pixel[x/mul_bx, y/mul_by] then correct_bits += 1 end end end @candidate[i] = correct_bits * 100 / (enlarged_width * enlarged_height) end return self end # importのときの比較結果をもとに文字を推測 # returns:: Fixnum or nil(失敗時) def guess digit, ratio = @candidate.sort {|a, b| a.last <=> b.last}.last digit = nil if ratio < 0 or ratio < 80 return digit end end end #= 画素マトリックスクラス # 画像ファイルを食わせると、各ピクセル(画素)を2値(black(1) or white(0))に # 変換して、内部で保持する。 # 以後、Matrixクラスのような感じで個々の画素にアクセスできる。 class PixelMatrix BLACK = 1 WHITE = 0 attr_accessor :width attr_accessor :height # width:: 幅 # height:: 高さ # is_flexible:: 自動的に伸張するか def initialize(width = 0, height = 0, is_flexible = false) @matrix = Hash.new {|hash, key| hash[key] = Hash.new(WHITE)} @width, @height, @flexible = width, height, is_flexible end # file:: 画像ファイル名のパス # brightness_threshold:: 画素を黒とみなす閾値 (0 - 255, default: 0x40) # returns:: self (DeCAPTCHA::PixelMatrix) def import(file, brightness_threshold = 0x40) gd = GD2::Image.import(file) @width, @height = gd.width, gd.height self.each_with_axis do |x, y| color = gd[x, y] greyscale = (color.red + color.green + color.blue) / 3 self[x, y] = (greyscale > brightness_threshold) ? WHITE : BLACK end return self end # reverse function of to_assoc def import_assoc(assoc) assoc.inject(0) do |y, columns| columns.inject(0) do |x, color| self[x, y] = color x + 1 end y + 1 end return self end # PixelMatrixを画像ファイルとしてexport # file:: 新たに作る画像ファイル名のパス def export(file) gd = GD2::Image::IndexedColor.new(@width, @height) gd.palette << GD2::Color::WHITE gd.palette << GD2::Color::BLACK self.each_with_axis do |x, y| gd[x, y] = { WHITE => GD2::Color::WHITE, BLACK => GD2::Color::BLACK, }[self[x, y]] end gd.export(file) return self end def to_assoc (0...@height).map do |y| (0...@width).map do |x| self[x, y] end end end # 指定された位置の画素を返す。 # returns:: PixelMatrix::BLACK(1) or WHITE(0) def [](x, y) if !@flexible and !in_range?(x, y) then raise RangeError end return @matrix[y][x] end # 画素に値を設定。 # returns:: PixelMatrix::BLACK(1) or WHITE(0) def []=(x, y, val) unless in_range?(x, y) then raise RangeError unless @flexible @width = (x >= @width) ? x + 1 : @width @height = (y >= @height) ? y + 1 : @height end @matrix[y][x] = val end def in_range?(x, y) ((0...@width) === x and (0...@height) === y) end # 指定された軸をもとに画素を走査し、Arrayに変換。 # 例えば、to_a(:vertical, 10) とすると、x == 10 な列を取り出して # Arrayにして返す。 # # axis:: 軸を指定 (:vertical または :horizontal) # pos:: 位置を指定。_axis_で指定した軸と直交する軸における位置を指定。 def to_a(axis, pos) {:vertical => lambda { (0...@height).map {|y| self[pos, y]} }, :horizontal => lambda { (0...@width).map {|x| self[x, pos]} }, }[axis].call end # returns:: Array def hline(y) self.to_a(:horizontal, y) end # returns:: Array def vline(x) self.to_a(:vertical, x) end # X軸方向に画素を走査。 # y:: どの位置で走査するか # returns:: 指定された軸の上にドットが存在: false, 無い: true def hline_blank?(y) hline(y).find {|color| color == BLACK }.nil? ? true : false end # Y軸方向に画素を走査。 # x:: どの位置で走査するか # returns:: 指定された軸の上にドットが存在: false, 無い: true def vline_blank?(x) vline(x).find {|color| color == BLACK }.nil? ? true : false end # 上下のマージンを削除した新しいPixelMatrixを返す。 # returns:: PixelMatrix def cutoff_vmargin pixel = PixelMatrix.new(0, 0, true) head = 0.upto(self.height - 1) do |y| break(y) if !self.hline_blank?(y) end tail = (self.height - 1).downto(0) do |y| break(y) if !self.hline_blank?(y) end head.upto(tail) do |y| y0 = pixel.height self.hline(y).inject(0) do |x, color| pixel[x, y0] = color x + 1 end end return pixel end def each_with_axis (0...@width).each {|x| (0...@height).each {|y| yield(x, y)}} end end end class DeCAPTCHA::Site::YourFileHost::Token DIGITS_ASSOC = [ # 0 [[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] ], # 1 [[0, 0, 0, 0, 1], [0, 0, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1], [0, 0, 0, 0, 1]], # 2 [[0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0]], # 3 [[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1], [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]], # 4 [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1], [0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1], [0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1]], # 5 [[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1], [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0], [0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]], # 6 [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], [0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0], [0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0], [0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0], [1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0], [1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0], [0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]], # 7 [[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], # 8 [[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0], [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0], [0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0], [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1], [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0], [0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], # 9 [[0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0], [0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1], [0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1], [0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1], [0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0], [0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0], [0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]], ] end __END__ Dir.glob('*.gif').each do |file| puts "Processing file: #{file}" p DeCAPTCHA.decode(file) end __END__
1. if/while/for/class 文に : (コロン)が必要
例)
while i<5:
i=i+1
print i
python はインデント、改行に意味を持たせることをウリとして開発されたわけで、
ここにはインデントがあり、それに意味を持たせられるのだから、: コロンなど必要なはずがない。
Why are colons required for the if/while/def/class statements?
http://www.python.org/doc/faq/general/#why-are-colons-required-for-the-if-while-def-class-statements
(意訳)ボブ「比べてみろよ?こっち
if a==b
print a
と、こっち
if a==b:
print a
どっちが読みやすい、スザンヌ?」
スザンヌ「まぁ、2番目のほうが読みやすいわ。」
んなぁことあるかーい!!変わらんわい!!
(追記)ボブ「あとは、英語の文法に似せたってのもあるんだ。」
: (コロン)の後に説明書くにしても10行は書かんだろがーー!!それに、似せたんなら、
if a==b,
print a
Python2.5のときは、新スタイルクラスのインスタンスは辞書のキーとして必ず使用できたけど、Python 2.6ではそういうわけじゃなくなってるぽい。という話。
class TestClass(object): def __init__(self, i_name): self._name = i_name def __eq__(self, i_other): if not isinstance(i_other, TestClass): return False return self._name == i_other._name print 'object.__hash__ = ' + str(object.__hash__) print 'TestClass.__hash__ = ' +str(TestClass.__hash__)
上記のようなコードを実行したら、Python 2.5とPython 2.6で結果が違いました。
Python 2.5の場合の結果。
object.__hash__ = <slot wrapper '__hash__' of 'object' objects> TestClass.__hash__ = <slot wrapper '__hash__' of 'object' objects>
Python 2.6の場合の結果。
object.__hash__ = <slot wrapper '__hash__' of 'object' objects> TestClass.__hash__ = None
Python 2.6だと、objectクラスを継承すると、__hash__がNoneになっちゃってる。このままだと辞書のキーとして使えないね。objectを継承したクラスで__hash__を実装すれば、大丈夫みたい。
「ハッシュ値の計算方法 (2)」というページも参考になりそう。