nullを受け付けないメソッドにnullを渡した時の挙動

っていう話を書こうと思ってたら、以前書いてた(cf. 前提条件を破った場合、どのような挙動をするのか? - 都元ダイスケ IT-PRESS

上のエントリにも書いたのだが、自分はこういう時IllegalArgumentExceptionを使うようにしている。そして、commons-lang v2.6 をよく使っていたので、その中の Validate#notNull を利用してnullチェックをするのが自分のスタイルだ。

しかし、いい加減commons-lang v3系に移行しようかな、と考えて色々触っていたのだが、v3から Validate#notNull の仕様が変わり、NullPointerExceptionを投げるようになったのだ。

といった辺りの俺周辺タイムライン → Javaでnullを受け付けないメソッドにnullを渡したらどうなるべきか - Togetter

で、いくつかのライブラリの引数チェックユーティリティの仕様を調べてみたのだが、以下の通り、統一感がない。。。

Apache commons-lang v2.6 Validate#notNull IllegalArgumentException
Apache commons-lang v3.1 Validate#notNull NullPointerException
Spring spring-core v3.0.5 Assert#notNull IllegalArgumentException
Google guava v10.0.1 Preconditions#checkNotNull NullPointerException

最後に、Facebook Questionsを利用してアンケートを取ってみたところ…。IAEの圧勝。

http://www.facebook.com/questions/309546165723428

色々考えて出した結論

自分が引数チェックを執拗に行うのは、アプリケーションコードというよりも、汎用ライブラリコード側だ。そういったライブラリについては、事実上Validateを使うためだけにcommonsに依存している状態だ。別に自分はcommonsにこだわりがある訳じゃない。
むしろ、汎用ライブラリはなるべく依存ライブラリが無い方が良いだろう。

というわけで、…ライブラリ毎にオレオレ引数チェックユーティリティクラスをIAE版として作って、コピーして使うのがいいのかな、と思った。

不可算名詞

懐かしいですね。受験の時勉強したなぁ。water, music, love等の名詞については、基本的に複数形を使わないよ、というアレですね。形がないものや抽象概念を表すので個数として数えにくいものや、集合全体の概念をとらえた単語なんかがそれに当たります。(参考:http://www.linkage-club.co.jp/ExamInfo&Data/uncountables.html

で、昨日プログラミングの話で「 getStudies():List 」なんていう話をした時に「勉強という意味でのstudyは不加算名詞。論文とかにする研究については加算だけど…」なんていう話を聞いて懐かしくなった。その場は「おお、そうなのか。昔習ったような忘れたような…w」という感じで getStudy() にしてみたのだが。

確かに「自然言語としての英語の世界」ではそうなのかもしれない。しかし自分は今、「勉強を数えられる、そして数えなければいけないドメインをプログラミングする世界」に居るので、数えても良いのではないか、なんて考えている。

私は変数名なんかを付けるとき、Collectionや配列型の変数にはなるべく複数形の名詞をつけることにしている。それによって「集合」と「要素」を直感的に扱えるから。しかし study について厳密に英文法に従うと、どちらも study という変数名になってしまう。つまり、実務上の小さな問題がある。

言語にとって一番大事なのは「文法上正確であること」よりも「表現力豊かであること」だと思うので、このような場合はむしろ studies という複数形を用いて「この世界では study を数えるのだ」という表明をすると良いんじゃないか。

ルールに乗っかって思考停止するのは危険だと思ってる。その場その場で最適解は異なるので「これがルールだから」で終わらせないように気をつけたいですね。まぁ、理屈っぽいとも言われるのだけどw

ソフトウェア開発者、完売いたしました

転職活動をはじめて2ヶ月弱。ようやく次の落ち着き先を決めました。ちなみに「転職したのに上司が変わらなかった」っていうネタも考えていたのですが、id:j5ik2o と行き先は別々になりました。まぁ、かとうさんは「永久名誉上司」として、永遠にエロ上司扱いしてやろうと思っています。あ、某D社の皆様も、早速エロ呼ばわりしてみると良いと思いますよ。喜ぶと思いますw

…さて。

正直、先日のエントリを上げる直前は、もうこの業界に自分の居場所はないかもしれない等と考え、薬屋への撤退戦略などを考えたりしていました*1。しかし、エントリを上げた途端当人らがびっくりするほどの反響を頂き、最初の1ヶ月は一つ一つお話を聞かせて頂くべく東奔西走していました。この真夏の陽気で外回りは結構体力的にも大変*2でしたが、この年になって社会科見学をしているようで、様々な勉強をさせて頂きました。本当に皆さん、色んな考えを持ってソフトウェア開発に向き合っているんだなぁ、と自分の視野の狭さが恥ずかしくなるようなこともw お話を聞かせて頂いた皆々様には、改めてお礼申し上げます。

しっかし、今回ばかりは自分の体が一つしかないことを恨みましたねw 各社様、本当に素晴らしい会社ばかりでした。しかし最終的に、自分の出来ること、やりたい事、求められている事など…そして直感によって、最もチャレンジングな決断をしました。

来月からクラウドスタディという会社にお世話になります。一言で言えば、従来孤独な戦いであった「勉強」を技術の力でサポートするサービスを展開する会社です。現在はスタディログというサービスを運営しています。スタートアップ企業のため、本当に取り組むことは多く、そして面白いことが出来る場所だと思っています。

というわけで、9月からは新しい世界です。クラウドスタディ関係者の皆様、改めましてどうぞよろしくお願いします。そして、友人各位、また、このブログを読んでくれている読者の皆様、今後とも今までと変わらぬご指導・ご鞭撻を宜しくお願いいたします。

あわせて読みたい 転職先が決まりました - じゅんいち☆かとうの技術日誌

*1:この話をすると「そんなバカなww」って言われるんですが、当人は本気だったんですよぉ。。。

*2:実は、4kgくらい痩せましたw リバウンドしないといいなぁ。。

献本御礼「Twitter API ポケットリファレンス」

レビューに参加させて頂き、献本頂きました。明日発売。

Twitter API ポケットリファレンス (POCKET REFERENCE)

Twitter API ポケットリファレンス (POCKET REFERENCE)

ちょっとばたばたしてしまって、まだ中身を全て読めていないのですが、レビューさせて頂いた原稿よりも格段に読みやすくなっていて驚きました。Twitter APIを利用したシステムを作るならば、是非手元に置いておきたい一冊じゃないでしょうか。

【転職活動】ソフトウェア開発者のバリューパックを発売、限定1セットのみ!

さて突然なんですが、私と上司(id:j5ik2o)は7月末を以て現職を退職することと相成りました。

まぁ、社内不仲とかクーデターとかそういうんじゃなくて、研究開発プロジェクトをクローズすることになったのであります。私が進めていた研究開発業務としては、みんなで結構頑張って企画を練っていて、その成果として、パイロット版を次週あたりにも社内向けにローンチできる!? とかいうタイミングだっただけに、非常に無念…。

で、このまま会社に残留してSI事業のメンバーとして居残る道もあったのですが、各所で囁かれているように、SIだけを続けていても近々食えなくなる日が見えてきていると思う。そして、都元自身としても、あらためて自分の想いを整理すると、そもそもSIをやりたいがためにこの業界に転進した訳ではないなと思いました。一言で言えば「なんかサービス作りてえよ」*1と思っているので、その道で頑張ってみたい。そんなわけで、残留はしないことにしました。

また、起業なんていう道もあるのでしょうけど、自分、経営者じゃなくて技術者ですねん。ガチで経営に打ち込むというのは自分のやりたいことではない、願わくば技術に集中したい、と思っているので、自分が経営の責任者になるという選択肢はありませんでした。

と、いうわけで、私と上司(id:j5ik2o)がセットで求職中、大売り出し中なのでありますよ。二人ともある程度Javaが書けますw

【セット内容】(なお、当人材はセットでのご雇用がお薦めですが、もちろん単体でのご雇用も可能ですので、ご相談くださいw)


で、両名の詳細については、個別にお気軽にお問い合わせください。

都元は…

希望としては、やはり製品やサービス(B2Cだとより嬉しい)を自社で開発している、または開発しようとしている会社に籍を置きたいと思っています。といっても既にサービスを回してる必要は無くて、これからやるんだけどエンジニアリングの手が欲しい…なんていうチャレンジングな会社さんも好きです。しかし、そういう会社さんとの出会いって「縁」なんですよねぇ…。良いご縁がありますように。

個人的な最近の技術テーマとしては、機械学習。特にレコメンデーションなんかに興味を持って取り組んでます。なお、私は見ての通りどこからどう見てもJava色に見えると思いますが、Javaでないお仕事にチャレンジする意志もあります。学ぶチャンスですよ。勉強やめたらそこで終わりなんで。

まぁこのブログを読んでくれている人は、大抵人事担当の方ではないと思いますので、技術者として「ウチの会社面白いと思うよ!」とか「ウチじゃないんだけどあそこの会社、トガってる人求めてたよ!」なんていう情報をコソっと教えてくれる囁き女将も募集中。もちろん、現在私たちと面識が無い方からのコンタクトも歓迎いたします。そこは自重などせず、是非に。 → 都元に「一緒に働こう!」と声を掛ける

しかしまぁ、こうやって売り出していれば仕事が来るなんて甘い事は思ってないので、こちらからも積極的に行きます。友人各方面には「御社ってどんな感じですかね」っていうお声を掛けさせて頂くこともあると思います。その時は、嫌がらずに是非お話を聞かせて頂ければ、と思います。宜しくお願いします。

ちなみに、前述の研究開発成果は、会社に残しておいても誰も引き継げないということで、都元個人が会社から譲渡を受ける手はずとなっています。そのうち、個人サービスとして回したり、もしかしたら転職先の会社でさらに練り込んでから事業化したり*2することがあるかもしれませんネ。


最後に、トライクレオで過ごした2年間、大変充実していました。一緒に頑張って来た仲間には、あらためて感謝いたします。どうもありがとう!

合わせて読みたい: 転職活動を始めました - じゅんいち☆かとうの技術日誌

*1:一言で言うと…軽いなぁw

*2:譲渡なので、こういった事態でも問題は起きません。ちゃんと書面を取り交わす予定。

オブジェクト指向のソースを読むのが難しい理由

ダラダラ書かない予定だよ。ざっくり行くよ。あと、分かってる人には当たり前な事だと思うよ。

あるクラスについて知りたかったら、まずその基底クラスを知れ

例えば、Integerクラスについて知りたいと思ったら、Integer.java だけを読んでいてはダメだ。確かに「Integerに特化した責務・構造・操作」は読み取れるかもしれないが、数値としての基本的な責務・構造・操作はNumberに書かれている。それを読まずして、Integerが保つ数値という一面を知ることはできない。Integer.javaには「Integer - Number」*1の情報しか書いてないのだよ。差分プログラミング。

さらに、忘れちゃいけない。Object.javaを読め。全ての道は暗黙的にObjectにつながっている。Objectを知らずしてJavaのクラスを知る事は絶対にできない。Objectなんて、みんな「知った気」になってるんじゃなかろうか*2。あと、クラスのextends関係だけではなく、当然インターフェイスのimplementsも必須。

ソースを読むのが難しいのは、出来るだけソースを読みたくないから「そのファイルだけに読む範囲を絞ろう」という甘えがあるからだ。知りたいクラスが使用*3しているクラスをも知るに越した事はないが、時間的制約もあるだろうから、せめて基底は押さえておく、という方針が良いのではないかとおもっちょる。

あるクラスについて知りたかったら、まずそのドキュメントを読め

トークンの並びを理解する前に、そのトークン列が何を達成しようとしてるのかを知らないと。というわけで、最初に読まなきゃいけないのは、実は「コード」じゃなくて「ドキュメンテーションコメント(Javadoc)」だ。

当然、メソッドやフィールドについているコメントも重要だが、もうほんとに最初に読むのは「型(classとかinterface等)に対するドキュメント」ね。結構分かった風になっちゃってるから飛ばすんだよね、みんな。

例えばStringBuilder。みんな、これを「文字列(string)組み立て(build)屋(er)」だと思ってるでしょ。まぁ、間違っちゃいないし、初心者連載では俺もそう説明しているんだけど。しかし、このクラスの第一義は「可変(mutable)な、文字(character)の、列(sequence)」だ。「屋(er)」を表すんじゃなくて、「列(sequence)」を表すクラスだったりする。Javadoc見りゃ1行目に書いてあることさ。

(あとはStringをネタにして深淵に潜り込むのもよし。これは本当に「文字列」か? 文字(character)の列(sequence)ならば、CharSequenceではないのか? そういう型もあるぞ? とか言って。あとはもうどうにでもしてクダサイ。俺はめんどくさいので燃料だけ投下して離脱しますw)

で、まぁこの例だと「だから何だ」になりかねないが。みんな名前だけで仕様と責務を想像して、その上で実装読んでたりしない? じゃなくて、ドキュメントで仕様と責務を把握した上で実装読むんだよ。

ソースを読むのが難しいのは、「英語を読みたくない」という甘えがあるからだ。英語ドキュメント読むよりトークン列を読むほうが楽だと、みんな思っているんだよね。僕にはわけがわからないよ。

*1:引き算ね。

*2:toString, equals, hashCode辺りが有名すぎてナメられがち。本当に知ってる?

*3:compositionとかuseとか。

Mahoutで分散レコメンド(3)


前回は 5ユーザ, 7アイテム, 21評価 という非常に小さいデータでした。さて、今回は大きめのデータを使ってみましょう。6040ユーザ, 3900アイテム, 100万評価です。

データの準備

GroupLensというラボが、評価データを公開してくれています。研究開発目的に限り無償で利用できるので、今回はこちらを利用させてもらいましょう。

MovieLens | GroupLens

このデータはMovieLensと言い、映画の評価情報の塊です。データ規模別に3つに分かれています。

  • 943ユーザ, 1682アイテム, 10万評価
  • 6040ユーザ, 3900アイテム, 100万評価
  • 71567ユーザ, 10681アイテム, 1000万評価(+10万タグ)

ここでは、(何の根拠もないですが)100万評価を選びました。「1M Ratings Data Set (.tar.gz)」ってのをDLしましょう。このデータはREADMEを除いて3つのファイルから構成されています。

  • movies.dat : 要するにアイテムマスタです。"::"区切りで、ID::タイトル::ジャンルですね。ジャンルは"|"区切りで複数指定です。
1::Toy Story (1995)::Animation|Children's|Comedy
2::Jumanji (1995)::Adventure|Children's|Fantasy
3::Grumpier Old Men (1995)::Comedy|Romance
...(略)...
  • users.dat : 要するにユーザマスタ。やはり"::"区切りで、ID::性別::年齢層::職業::郵便番号です。今回はあまり使いません。
1::F::1::10::48067
2::M::56::16::70072
3::M::25::15::55117
...(略)...
  • ratings.dat : これが評価データです。ユーザID::アイテムID::評点(1〜5)::タイムスタンプ(epoch)
1::1193::5::978300760
1::661::3::978302109
1::914::3::978301968
...(略)...

で、rating.datは、このままの形でMahoutのRecommenderJobに食わせることができません。フォーマットが違います。ユーザID,アイテムID,評点(float)って感じなので、まぁコードでも書いて上手く変換してください。

BufferedReader in = null;
BufferedWriter out = null;
try {
  in = new BufferedReader(new InputStreamReader(new FileInputStream(INPUT)));
  out = new BufferedWriter(new FileWriter(OUTPUT));
 
  String buf;
  while ((buf = in.readLine()) != null) {
    // UserID::MovieID::Rating::Timestamp
    String[] split = buf.split("::");
    out.write(String.format("%s,%s,%s%n", split[0], split[1], split[2]));
  }
} finally {
  Closeables.closeQuietly(in);
  Closeables.closeQuietly(out);
}

で、この変換後のファイルをratings.txtと呼びましょう。

データの追加

まぁ、ratings.txtを使って、ユーザ1〜6040の誰かに対してレコメンドを計算させてもいいんですけど、それだとなんだかつまらない。ということで、自分の見た事ある映画の評価情報も混ぜてみました。

9999,47,2
9999,50,5
9999,126,3
...(略)...

意外とこのデータ作るのは大変だと思いますが、実際回してみる際は是非、やってみると面白いと思います。こいつをmyratings.txtとしましょう。ユーザIDの9999は自分です。

そしてusers.txt(前回参照)も用意しましょう。

9999

いざ計算

まずは必要なデータをHDFSに転送。

前回のレコメンドを回した人は、inputディレクトリに既に既に色々ファイルがあると思いますが、一回全部消しましょう。outputディレクトリやtempディレクトリなども出来ていると思いますが、これも存在すると勝手に上書きはしてくれないので、消しておきましょう。

$ hadoop fs -put /path/to/ratings.txt input/ratings.txt
$ hadoop fs -put /path/to/myratings.txt input/myratings.txt
$ hadoop fs -put /path/to/users.txt users.txt

ちなみに、前回はHadoopの起動コマンドラインに入力ファイルを指定しましたが、今回はファイルが複数なので、入力ディレクトリとしてinputを指定します。このinputディレクトリ内にある全ファイルを読み込むため、inputディレクトリにこれ以外のファイルが入らないようにしましょう。上記の例でもusers.txtはinputディレクトリに入れてません。

$ hadoop jar /path/to/mahout-core-0.5-job.jar \
        org.apache.mahout.cf.taste.hadoop.item.RecommenderJob \
        -Dmapred.output.dir=output \
        -Dmapred.input.dir=input \
        --usersFile users.txt \
        --similarityClassname SIMILARITY_PEARSON_CORRELATION

今回はこんな感じ。前回は5分で済みましたが、今回は77分かかりました。結果はこんなかんじ。

$ hadoop fs -cat output/part-r-00000
9999    [1038:5.0,1565:5.0,2638:5.0,3184:5.0,3106:5.0,2063:5.0,3092:5.0,2049:5.0,630:5.0,37:5.0]

元の評価データが1〜5の整数だったのでバリエーションが少ないのかな…。アイテムベースだからか…? なぜか全部5.0という予想評点でした。まだまだ5予想のアイテムが数多くある中の10個なんでしょう。100個でやってみても全部5.0でした。なんか微妙に納得いきませんが…。とりあえず挙げられた映画をリストアップ。

1038::Unhook the Stars (1996)::Drama
1565::Head Above Water (1996)::Comedy|Thriller
2638::Mummy's Tomb, The (1942)::Horror
3184::Montana (1998)::Action|Comedy|Crime|Drama
3106::Come See the Paradise (1990)::Drama|Romance
2063::Seventh Heaven (Le Septième ciel) (1997)::Drama|Romance
3092::Chushingura (1962)::Drama
2049::Happiest Millionaire, The (1967)::Comedy|Musical
630::Carried Away (1996)::Drama|Romance
37::Across the Sea of Time (1995)::Documentary

ふーん…。まぁ、いまいち映画に興味がない都元でありました。

さて、次回は

次回は疑似分散じゃなくて本格的にこいつを分散させてみます。Amazon Elastic MapReduce っていうサービス(有料)を使いますので、実際に試してみたい方はAmazon Web Servicesの以下のサービスを申し込んでおきましょう。

追記

と、思ってたら、ボヤっとしているうちにsuz-labさんに先を越されてしまいましたwww 続きはあちらで(ぉ