ねこでもわかる Mercurial

社内の Tech TalkMercurial 入門な話をする機会があったので、その時のスライドを公開しました。前回の記事 id:nyaasan:20110309 とは違って "本当の意味で" 入門者向けの内容になっています。

http://www.slideshare.net/nya3jp/mercurial-for-kittens

しかし、入門者向けといっても実際のコマンドに関する説明はほとんどありません。実際の使い方はインターネット上に沢山良い資料がありますし、なにより hg help を見るのが一番ですのでそちらを参照されることをおすすめします。このスライドは、それらのドキュメントを読む際に大きな助けになるであろう、Mercurialリポジトリの履歴をどう扱っているかに関する解説をメインとしています。また、Git を既に使っているが Mercurial は使ったことがない、という人も多そうだったので、Git ユーザ向けのポイントもいくつか書いています。

Mercurial に入門してみる

どうも、お久しぶりです。我ながらこのブログの更新頻度の低さはひどいものですね。ここ数回の記事が ICFP ネタ(しかも1年おき)というのもどうかしているとしか思えません。

しかし今日なんとなくここの存在を思い返したので、ちょっとたまには技術的な記事でも書いてみようと思います。


というわけで Mercurial 入門です。といっても裏口から忍びこむ感じなので、ごく普通に Mercurial を使い始めたいという方は別の解説ページにあたることをおすすめします。


Mercurial はご存知の通り分散 SCM の一種です。似たソフトウェアには Git, Bazaar などがあります。では Mercurial はどういう点でそれらと違うのか、と聞かれると、実はあまりよく分かっていなかったりします。というのも恥ずかしながら Git や Bazaar をあまり使ったことがないからです。Git はちょっと使ってみようと思ったことが幾度と無くあるのですが、日常的に使う機会がないこともあり、どうやって動いているのかまだ理解できていません。

しかしながら話を聞く限り Mercurial にしかないであろうと確信している特徴を挙げると、次の 2 つになります。

  • データ構造がシンプル
  • Python で書かれている


データ構造に関しては Mercurial: The Definitive Guide の一節 を読めば裏で何が起こっているかなんとなく想像がついてしまうほどシンプルです。Mercurial を使うすべての人が読むべき文書だと思います。

二つ目の Python で書かれているということに関してはあまりぱっとしないかもしれません。ユーザにとってそれがどの言語で実装されているかが重要であることなんてほとんどありません。あるとしたら処理速度くらいで、その点でいえばスクリプト言語で実装されている Mercurial は不利でしょう。しかし、少なくとも一つアドバンテージがあるのです。それは何かというと、この記事の本題なのですが、スクリプト言語から API 呼び出しが簡単にできることです。


前置きは置いておいて、とりあえず入門らしいことをしてみましょう。やる内容は、

  • 新しい Mercurial リポジトリを作成
  • README という名前のファイルを作ってコミット
  • ファイルがちゃんとリポジトリにコミットされたことを確認する


だけの簡単な作業です。

まず、Mercurial をシステムにインストールしておきます。Debian/Ubuntu なら apt で mercurial パッケージをインストールするだけです。そして、python インタプリタを立ち上げます。

[nya@bardiche ~]% python
Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>

そして mercurialPython モジュールをロードします。後ほど使うので、mercurial.ui.ui クラスのインスタンスを作っておきましょう。

>>> import mercurial.hg
>>> import mercurial.ui
>>> ui = mercurial.ui.ui()

まずは Mercurial リポジトリの作成です。場所は /tmp/hgtest にします。

>>> repo = mercurial.hg.repository(ui, '/tmp/hgtest', create=True)

これで /tmp/hgtest にリポジトリが出来ました。とても簡単ですね。とりあえず何かファイルを追加してみましょう。

>>> f = open('/tmp/hgtest/README', 'w')
>>> print >>f, 'Hello, Mercurial!'
>>> f.close()
>>> repo[None].add(['README'])
[]
>>> repo.commit('first commit', user='nya')
')\x9dt:\x94\xd9T\x82J\x10\x9da\xe2fB\xef\xab\x9do\xc6'

これでコミットできました! 最後に出てきたバイナリ文字列はこのコミットのIDです。

実際にコミットされていることを確認してみましょう。まずリポジトリの tip を取ってみます。

>>> head = repo.changelog.tip()
>>> head
')\x9dt:\x94\xd9T\x82J\x10\x9da\xe2fB\xef\xab\x9do\xc6'

さきほど帰ってきたバイナリ文字列と同じですね。これを使ってまずコミットの内容を表すデータである changelog を取ります。

>>> ch = repo.changelog.read(head)
>>> ch
('\x8eB\x0c\xf3+\xb7\x03\x1c\r\xc7\x9a\xe6\xe9\x07\x19w\x82\xde?\xd1', 'nya', (1299683052.0, -32400), ['README'], 'first commit', {'branch': 'default'})

ほとんどの要素の意味は見れば想像が付くと思います。1番目のバイナリ文字列はマニフェストのIDです。マニフェストとはあるリビジョンにおけるファイル名とその内容を関連付けるデータです。読み込んでみましょう。

>>> mf = repo.manifest.read(ch[0])
>>> mf
{'README': 'Q\x82\xf8\xe0\xaaIn\x87r\xf8\x0b>\x07\x86eL3\x8d\xb4\xd3'}

この辞書の値になっているのが、このリビジョンにおけるファイルの内容を示すIDです。これを使うことでファイルの内容を取得することができます。

>>> repo.file('README').read(mf['README'])
'Hello, Mercurial!\n'

ちゃんと書いた内容がコミットされていることが確認できました。

短いですが今回はここまでです。また気が向いたら続きを書くかも?

ICFPC2010 解説スライド

社内の休憩時間で今年の ICFPC についての話をする機会があったので、その時のスライドを公開します。

http://www.slideshare.net/nya3jp/icfpc2010-purepurecode

ICFPCを知らない人向けに作ったつもりなのでアルゴリズムの詳細などには踏み込んでいません。詳しい話はチームメンバーのみなさんが書いてくれているのでそちらを参照してもらえればと思います。rieszさんが まとめページ を作ってくださっているので、そちらから辿れます。

ICFPC2010: PurePureCode++ Mini Presentation

In the past week I gave a mini presentation about ICFPC2010 to my colleagues. This includes a brief introduction of ICFP programming contest, this year's problem and what our team did in the contest. Since the presentation is intended for audience who are not necessarily familiar with the contest, it does not go into detail, such as our solvers' algorithms. For more details, @chunjp, a member of our team, has written an English write up and you may find it interesting.

http://www.slideshare.net/nya3jp/icfpc2010-purepurecode

ICFPC 2010 参加まとめ

気が付いたら前回の日記がちょうど1年前の ICFPC 参加記だという……。そろそろ日記じゃなくて年記に改めようかと思います(嘘)。

というわけで、今年も ICFPC に参加していました。チーム名は ぴゅあぴゅあこーど++ (pure pure code ++) で、メンバーは @chunjp @gusmachine @izumi_yusuke @nya3jp @phoenixstarhiro @tanakh という、日本競技プログラミング界のエースを集めてきたようなチームでした(自分除く)。

今回の問題はちょっと説明が大変なので、問題文原文konnさんによる日本語訳を読んでください、と省略。

今回、基本的に僕はひたすらチームの生産性向上と仕事の自動化に取り組んでいた感じでした。今回のコンテストのレギュレーションだと、大量に市場に出てくる車に対してかたっぱしから燃料を供給する必要があります。そこで我々のチームは、手持ちのソルバで解けるものは自動的に解いて解答を投稿して、解けなかったものに関しては人間が手作業で問題を解析して新しいソルバを作る、という流れを作っていました。僕がやっていたのはまさにこの辺のワークフローの整備と、それに必要なインフラの整備です。今回のコンテストではこのような役割の人間が必要だったんじゃないかと思います。

逆に言うと、ternary encoding の解析であるとか、ソルバのアルゴリズムであるとか、そういったところは(もちろん最低限の sync はチーム内でやりましたが)はっきりと分かっていなかったりします。その辺の解説はきっと他のメンバのみなさんがやってくれると思うので任せます。

ここからは僕の視点からのタイムラインでまとめたいと思います。

19日 0時

やっと帰宅してチームの Skype チャットに参加。問題文の読解と解釈に1時間弱くらい。しかし問題文を読んでも問題が分からなかった。「仕様を推測しろ」多すぎ……。しかもただでさえ論理回路とかは不慣れなのに、3値論理とか言われても困る。
既にチームが論理回路の書式の推測は完了してくれていたので、目下の問題はゲートの真理値表を求めることだったが、これも数時間程度で @phoenixstarhiro さんが決定してくれた。以後こんな感じで何度も若者の人間離れが危惧される事案が発生することになる。
あとはゲートを組み合わせて基礎回路を作ろうということになったけども、この辺で眠くなり脱落。

19日 9時

起きて Skype ログをみたら @tanakh さんが回路ジェネレータを作って219番の車を通していた。さすが。
とりあえず @phoenixstarhiro さんのお宅に向かってチームに合流。一通り sync して、次は燃料の ternary encoding の推測に取りかかることに。
ここらへんで各自がばらばらに燃料を投げて推測していたのだけど、これだと結果の共有が出力を Wiki に貼りつけるくらいのことしかできずいかんなあと思い、Mechanize でサブミットを代行するサーバとコマンドラインインターフェースを作り始めることに。この選択は結果的にとても良かったと思う。

19日 17時

車と燃料の ternary encoding が判明したので、とりあえず手動で解いて解答を投げ始めることに。ternary encoding のエキスパート @phoenixstarhiro さんと @izumi_yusuke さんに手伝ってもらい、Web インターフェースに車を不等式に変換して表示する機能と、行列を入力すると燃料に変換して投稿する機能を追加。@chunjp さんと @tanakh さんはソルバを書き始め、@gusmachine さんは車の作成にとりかかっていた。
はやめに @chunjp さんのブルートフォースソルバが出来たので、しばらくはそれを各自の手元で走らせて、出力の行列を手で Web に貼りつける作業。しばらく6位前後をふらふらしてチーム名をスコアボードに晒して注目を引いたりしていた。

19日 20時40分

Lightning Division があと20分で終了というところで、@tanakh さんが焼きなましソルバを完成させる。「無理ゲーと思っていた問題が運ゲーに変わった」。全員で全力でプログラムを走らせて投稿祭りを開催し、まもなく終了。この時点では5位以上だったみたいだけど、あまり上位ではなさそう。みんなでご飯を食べて帰る。
僕は家に帰ったあと、Web インターフェースをソルバを走らせるサーバとは別のところに引っ越した。実は後にデータ量がふくれあがってCGIがものすごいCPUリソースを食うようになるので、これも正しい判断だった。

20日 13時

ふたたび @phoenixstarhiro さん邸に集まって2日目開始。ここまでは数字の ternary encoding をテーブルで持っていたのだけど、車が増えてきて手持ちのテーブルではパースできないものが出てきたので、再び ternary encoding 班ががんばりエンコーディング規則を一般化。
そろそろ自動ソルバを走らせないとやってられなくなってきたので、適当なシェルスクリプトを組んで自宅サーバでがんがん走らせ始める。

20日 15時

燃料事業に徹してきた我が社が @gusmachine さんの尽力によりついに車事業に進出。以後コンスタントにリリースされていった。

20日 16時

特定の車種を狙い撃ちするソルバが作り始められる。解いているチーム数が少ない車、特にそれを作ったチームしか燃料を供給していない車を解くと、相手の収入源を奪えるのみならず自分にも大きな点が入るので、こういう積み重ねが後々効いてくることになる。この日は手動で他のチームの車を解析してこういうソルバを作成したり、手で提出したりしていたのがほとんどだった。
それにともなってソルバが増えてきたので、仕様を決めてソルバを簡単に追加できるようにした。@chunjp さんも自動ソルバをがんがん走らせ始める。

20日 20時

解いていない車を、他のチームが解いている数や解けやすそうに見えるかどうかで適当にスコアリングして、それに従って自動ソルバにスケジューリングするように変更した。
ほどなく解散。

21日 10時

起きてみたら公式サイトの車一覧にページングが導入されて、スクレイパーが止まっていた……。とりあえず新たに提供された2つのページから車の情報をスクレイプしてくるように変更。更新が終わってみると車が大量に増えていて、CGIを1回呼び出すのに4秒くらいもかかってしまっていたので、キャッシュを付けて対応。
あと、チームロゴを @tanakh さんが作ってくれたのでページに貼るなどした。
この辺の作業を自宅で取り急ぎやってからチームに合流した。

21日 15時

キャパシティいっぱいの車のリリースが完了。
以降は、ひたすら他者の車の解析&ソルバの作成をしていた。僕は、新たに作ったソルバを適用するために、各車の制約式を grep して狙い撃ち対象を絞るコマンドとかをこつこつと作成してサポート。「すごい勢いで廃車にされていく」「リロードする間にsolvedが50個増えた」など驚きの声が続々と。

21日 18時

自動ソルバがほとんどのケースを勝手に解いてしまうので、人手で解けそうな車を見つけて焼きなましで解答を見つけて提出するまでの間に、自動ソルバが既に解いていた、というケースが頻発。「車はすぐに焼けてしまう」

21日 21時

終了。最後の数時間は単純作業ばっかりですごく長かった……。

スクリーンショット

最終的なインターフェースのスクリーンショットはこんな感じになりました。トップ5に入っているのでスコア関連の部分は消してあります。あと、ロゴ部分は諸般の事情でお見せできません……。

http://nya3.jp/tmp/icfpss4s.png

まとめ

とにかく楽しかった! 最初の ternary encoding 解析とかはプログラミングコンテストじゃなくてパズルゲームじゃないか、という批判もあるようで、確かにそれにも一理あるのですが、とりあえず僕個人としてはそういうのも大好きなので楽しめました(といっても解析はほとんどチームメンバの人がやってくれたんですが……)。他にも、うちのチームはあまり手を回せていませんでしたが、3値論理回路の圧縮とかはチャレンジングで面白いと思います。何より、普段はなかなか一緒に一つの問題にとりかかる機会がないような人たちと、72時間のあいだ一緒にコンテストに参加できるというのはすごく楽しくて、ICFPCの醍醐味だと思います。
サーバが落ちたり、いくつか問題があったのも事実ではありますが、今年の運営の方はかなりの労力を割いてくれたのではないかと思います。感謝です。

さて、気になるのは最終順位ですが、けっこういい線を行っていると予想しています。最終結果が発表されるまで具体的な数値はあまり言わない方が良さそうなのでやめておきますが、各個撃破ソルバのおかげで2チームしか解いていないような車種の大部分をうちのチームが掌握していたので、後で計算してみた periodic revenue は相当な値になっていました。あとは、2日目あたりで既に上位に居たチームにどれだけ追いつけているか、というところです。勝てているといいなあ。

Operation Clear Sky

先の6/28-30日にかけて,毎年恒例のプログラマの祭典 ICFP Programming Contest (ICFP-PC) が開催され,私も参加していました.

こんな辺境の日記を読んでいる人ならほとんどが知っているかもしれませんが,これは ICFP という関数型プログラミングに関する学会に併設して毎年開かれているコンテストです.プログラミングの腕を競うコンテストというのは最近では一部の界隈でそこそこメジャーになってきていて,たとえば有名どころでは ACM-ICPC, TopCoder, IOI, SuperCon などがそれぞれの競技ルール・参加資格の設定のもとで開かれています.

そのように多種多様なプログラミングコンテストの中でも,特に個性的なコンテストの一つが,今回開かれた ICFP-PC です.その特徴はどのようなものかというと...

  • 好きな人同士でチームを組んで参加します.参加資格に特に制限はなく,学生であるか社会人であるかや年齢などを問わず,参加したいという意思さえあれば誰でも参加することができます.
  • オンラインで開催されます.決められた時間に問題が公開され,世界中のチームが一斉に競技を始めます.解答の提出やランキングのチェックなどは全てウェブサイト上で行います.
  • 競技時間は72時間です.これが ICFP-PC を ICFP-PC たらしめている大きな要素の一つです.
  • 「正解」の無い問題が出題されることがほとんどです.たとえばあるゲームのプレイヤーAIを作成し,参加者のAI同士で戦わせて成績を競う,などの問題が過去に出題されました.

特に競技時間が72時間であるところが一番の特徴ではないでしょうか.チームによっては徹夜でコードを書いたり,チーム内でメンバーの起きている時間帯を分けて,72時間誰かしらが起きて作業をするように戦略を立てたりするそうです.毎年,コンテストの日程は土日に被るように設定されますが,どうしても1日ははみ出してしまうので,熱心な社会人プログラマの参加者はコンテストのために有給を取ったりします.


さて,今年の問題はどういうものだったかというと,大雑把に言って,地球の衛星軌道上を回っているデブリを宇宙船を操作して回収しろ,というものでした.問題文は ICFP-PC のホームページから見ることができるので詳しくはそちらを参照してもらいたいのですが,もうちょっとだけ詳しく書くことにします.

2次元で表された宇宙空間の真ん中に地球があり,その周りをいくつかのデブリが衛星軌道にのって回っています.デブリは離散時間で近似されていること以外は高校物理で習った通りそのままの動きをします.また,自機となる人工衛星も同じ物理法則のもとで宇宙空間に浮かんでいます.この衛星は装備している推進装置を使って動かすことができます.ただし,推進装置を使うには燃料が必要で,燃料以上の推進力を得ることはできません.また,地球に衝突してしまってもだめです.目的は,推進装置を使って衛星をデブリの軌道に誘導し,デブリを回収することです.宇宙空間には複数のデブリが浮かんでおり,なるべく多くのデブリをなるべく早く,なるべく少ない燃料を使って回収するとスコアが高くなります.

推進装置を使って瞬間的に加速したりすることができるため,結構簡単に制御できるのかな,などと最初の頃は考えていたのですが,実際にとりかかってみると決してそんなことはありませんでした.軌道と軌道の間を遷移するのが結構大変で,完全に真円の軌道だけ取るならまだしも,楕円軌道となるとおいそれとは行きません.それに,実は地球の他にも月が浮かんでおり,月との間で働く引力のせいで軌道が完全な楕円にならないことや,シミュレーションが離散的な時間で行われるために理論的な計算通りに動かすことができないことなど,いろいろと罠がありました.デブリと同じ軌道に乗ろうとしても,1秒だけ計算と違う時間に到達するだけで相手と数kmも離れてしまったりするので大変です.本職で宇宙船の制御をやっている人たちの偉大さを痛感しました.


さて,ここからは参加記になります.問題は上で説明したもの(Operation Clear Sky)だけでなく,いくつかの難易度に分かれていて,順に解答を作成していくことで最後の Operation Clear Sky を遂行するのに必要な道具が揃うようになっていました.ここからは問題文を読まないと意味が分からないので,コンテストに参加されていない方は 問題文 を読んでからどうぞ.あと,コンテスト期間中の様子は chunさんのエントリ にとても簡潔にまとまっています.


私はチーム Intercaml の一員として参加していました.メンバーは chunさんgusさんphoenixさんと私の4人です.去年の ICFP-PC に一緒に参加したよしみで仲間に入れさせてもらいました.それぞれ,軌道制御プログラム,理論計算,ビジュアライザ,基礎VMといった感じで自然に分担が分かれました.

コンテストの1日目はそれぞれオンラインでやっていました.当初,私はチームに入っておらず,飛び入りのような形で昼の12時くらいからチームに加えてもらいました.私は午前中のうちにアドホックに実装したVMを持っていたのでそれを持ち込み,とりあえず Wikipedia のホーマン軌道遷移などのエントリを読んで理論の勉強から始めました.意味もわからないままとりあえず書いてあった式をそのまま実装したところ,なにやらうまくいってしまったので,そのまま100xのソリューションを提出したのですが,なぜか CRASHED! という結果が返ってきてしまい,途方にくれるはめに.どうやら出力フォーマットが間違っている時もクラッシュしたという表示がでるらしく,ICFPIRCチャンネルで同じような人を探して原因を見つけ出しました.
その後しばらくすると,200xに取り掛かっていたgusさんがいいところまで行っているようだったので,ふたりでコードをデバッグしたりしていました.バグを直してなんとか動かすことができたのですが,実際に実行してみると,大体目標と同じような軌道に乗せられるものの微妙に位置がずれていて終了条件を満たせません.このあたりから,時間的な離散化誤差という悪魔が片鱗を見せ始めます.とりあえずここは,私がアドホックに相手の衛星を追いかけるコードを書いてなんとか相手とランデブーさせることに成功.200xも無事に解くことができました.
そして次の300xのハードルが高かった.最初の印象としては,楕円軌道と円軌道じゃそれほど難易度は変わらないのでは,と思ってしまったのですが,軌道同士を遷移させるという観点から考えると,円と楕円では難易度にかなりの差があります.うちのチームの方針としては,とりあえず自機の軌道を円軌道に移して,さらに相手の軌道に接する別の円軌道に移り,そこからランデブーするという方針を採りました.ちょうど,100xと200xを組み合わせた感じです.このコーディングはけっこう難航したのですが,担当していたchunさんが気合で書ききり,夜中になったころにはじめてランデブーを成功させてくれました.chunさんの神性はこの辺から発揮されていたように思います.これの最適化をがんばったところ,ランキングで2位まで浮上し,チームは大いに沸きました.あと50点でトップになれたのだけど,残念ながらそこまでは至らず,そのままLightning Roundの終了時間である24時間目を迎えました.

2日目はphoenixさんの家に全員が集合して,顔を突き合わせてのコーディングになりました.残りの400xを解くにはあと二日間も時間があるということで,ゆっくり確実にとりかかることにしました.あと,この日に至ってはじめて月が存在することが明かされました.月の引力の影響で,自分とデブリの軌道が楕円からずれることになるため,誤差がもっと大きくなることになります.
私は2日目の朝からVMの書き直しに取り掛かりました.おそらく今後,VMを複製して軌道の予測を行ったり,よりよいコースを探索したりするだろうと思ったので,なるべくそういった処理が簡単になるようにクラスをまとめました.あと,自機やデブリの速度を求めたりするルーチンもVMの実行系に埋め込んでしまいました.速度や加速度の関係式が仕様で定められているので,この辺は楽にできました.
この日はいままで書いたコードのリファクタリングとか,phoenixさんが仕上げてくれたビジュアライザでデブリの軌道を見たりして作戦を立てるのがメインでした.夜ご飯は近くのファミレスに行って,食べるついでに議論タイム.みなさんの頭が良すぎるのでついていくのが大変でした.
夜になってからchunさんとphoenixさんによって少しずつ400x向けのコードが書き始められ,深夜に初提出.しかしあまりスコアは伸びず,けっこう難しい気がしてきました.

3日目は私以外はやはりphoenixさんの家に集合.私だけはこの日に大学で外せない輪講があったので自宅から参加しました.
ここから,理論的な計算と実際との誤差を修正するためのアルゴリズムを導入することにしました.使う推進力を理論計算で求めた後,VMを複製してその推進力を加えた時の結果のシミュレーションを行い,目標とシミュレーションのずれを考慮して推進力に修正を加えるというものです.たとえば自分とデブリの速度ベクトルが真逆でとても速い相対速度で交差するような場合,そのデブリを回収できるかどうかは整数時間において相手との位置がたまたま近づいているかどうかに依存し,ほとんど運次第となってしまうのですが,この方法を使うことでうまくいく軌道を探して採用することにより,ほぼ必ずデブリを回収することができるようになります.私たちのチームではこの手法をTASさんと呼んでいました.ある意味で自然なアイデアなので,たぶん上位のチームはみんなやっているんじゃないかなと思います.
あとは誤差との戦いでした.予測と誤差が出るほど,燃料を多めに使ってしまうはめになります.燃料は基地に戻れば回復させることができるのですが,燃料を無駄づかいするとその分のペナルティと回収しに行く時間のコストによりスコアが大きく下がってしまうので,なるべく燃料は節約しなければなりません.この問題は最終的に,大きな誤差を与える存在である月からの引力を打ち消すように常に推進剤を使い続けることで解決しました.推進剤を使い続けることで燃料は消費してしまうのですが,実はトータルでそれほど必要量が多くないので,誤差によって無駄に燃料を使うくらいならこちらのほうがよいのです.これでだいぶ燃費が改善し,月軌道上のデブリ以外は回収することができました.
最後にchunさんが月にデブリを取りに行くコードに取り掛かってくれました.奮闘の結果ついに月に到達し,全デブリを回収することができたのですが,残念ながらトータルスコアが悪くなってしまったのでそのコードは反映されませんでした.とても残念.


私にしては長い文章になってしまいましたが,一言でいうと,かなり楽しかったです.やはり複数人で集中的に同じ問題に取り掛かるというのは楽しいですね.私はもう高校物理すら覚えていないのですが,チームメンバーのみなさんが頭のいい人たちばかりなのでとても助かりました.その代わり,私はVMの方でチームにコミットできたかなあ... と思っています.おかげさまで順位的にも良いところに行けていると思います.結果が楽しみ.