TECH PLAY

株式会社LIFULL

株式会社LIFULL の技術ブログ

656

どうも上津原です。 今回は、実装時に初心者だから(だと思う)つまずいたポイントと、どう解決したかをUnity操作、Oculus Rift対応それぞれに分けて紹介していきたいと思います。 教える立場の人は、完全初心者が3Dゲームに手を出すとどういうところにぶつかるのか? これから始める人は、ぶつかる前にどこを押さえておけばいいのか? それらの参考になれば嬉しいです。 では、Unity操作編、始まりです! 3Dゲームの基本 3Dゲームには、Z軸や重力、当たり判定、ライティング、カメラ…いろいろな要素が絡みます。 そんな中、盛大に転んだものを3つお伝えします。 オブジェクトが地面を通り抜けて落ちていく! 本をひと通り試した私は、ある程度もう知ってるもんね〜という心持ちでプロジェクトを作っていました。 そこで一番最初のつまづきがきました。 「この3Dモデル配置してRigitbodyで重力付きの物が作れるんでしょ?簡単簡単!」 そしてひたすら落下し続けるキャラクター! 「 アイエエエエ!ノラナイ!?ノラナイナンデ!? 」 地面にオブジェクトが乗らない!!! 読んだ本ではこんなことはなかったので、かなり頭を悩ませました。まさかこんな落とし穴があるとは(うまい!) Unity内で生成したもの(CubeとかShpereとか)なら、予め当たり判定が付いているのでうまく動いていました。しかし、ただの3Dデータを外部から持ってきて配置したらコライダーがついていません。 モノの上に乗るには「 コライダー 」が必要。 そして当時私は、 Rigitbodyをつけることが「ものに乗れる条件」 だと思っていました。まぎれも無いバカです。 なので私は 「どちらともにコライダーがなければものの上に乗らない」ということに気がついていなかった し、なのでそもそも 「上に乗ること」を「衝突」 だとも考えもしませんでした。 コライダー?メッシュ?シェーダー?なにそれ。 沢山出てくる専門用語。みんなアプリ開発では聞きもしませんでした。 「Unity すりぬける」でググった私が出会った言葉、それが「コライダー」でした。 「コライダー」と聞くと、ライダーの方に意識が取られて、ライダー?とか思ってました。 そして「コライダーとは」でググッた結果、それが 「あたり判定」 だと知りました。普段ゲームをやるにあたって「あたり判定」という言葉は馴染みがあったので、この言葉を知ってやっとピンときました。 どうやら2つのオブジェクトお互いにコライダーがないと、ものには乗れないらしい。それを知り実践し「なるほど、コライダー」とやっと理解したのでした。 ほかにも、メッシュって?シェーダーって?レンダラー?トランスフォーム??それぞれの専門用語を一個一個調べ、何なのかを理解していきました。 代表的な専門用語は、予め調べておく コライダー メッシュ シェーダー レンダラー トランスフォーム 解説記事にもついていけなくなるのでせめて上記のこれらくらいは把握していたほうがいいと思います。 画像検索で出てくるようなライティングが出来ません! 検索して出てくる、Unityで出来たゲーム画面画像を見ては「これ、俺の持ってるUnityと違う…」状態でした。 ライトを配置し影を描画してもダサイ感じ…。「Proの無料体験版だから、じつは機能制限されてるとか!?」「特殊なプラグイン買わないと出来ないとかじゃないの?」とも考えました。 理想 現実 (画像: Unity Manual より) そこで出た答えが「 Lightmapping 」 ライトマッピングはまだ全然把握できていないし、使いこなせていないのでここでは偉そうなことは書けませんが、Lightmappingを利用することでやわらかな影の描写や、動作を軽くすることなどが実現できるようになりました。 コード iOSのアプリ開発をやっている中、考えもしない要素がコードにも山積みです。そもそもコードはどういう立場にいるのか?コードでしか出来ないことは? 4つのつまづきポイントを紹介します。 UnityScriptイコールJavaScriptではない!? そして、舞台はコード実装へ。 UnityはJavaScriptで開発できるんだよ!すごいでしょ!という触れ込みと、最初に読んだ本のコードがJavaScriptだった事もあり、それを使って開発を進めていくとすぐに気が付きました。 「これ、俺が知ってるJavaScriptと違う…。」 (またかよ) 「型宣言もしなくちゃいけないし、WebのJavaScriptで使ってた関数を使えない時あるし、 そういえば俺、そもそも知ってたのjQueryだわ。 」 UnityScriptはUnityScript JavaScriptちょっとかじってたしなんとかなるだろ、と思って安易にJavaScriptを選んだけど、結局はきちんとUnityScriptとしてJavaScriptを覚え直す事になりました。 僕は本当にかじった程度だったのでもっとちゃんと知っている人ならばそうではないのかもしれませんが、僕にとっては違うものに感じました。 遠隔にあるものを認識するには? ものの持ち上げ判定を得るために、視線の先に対象のものがあるのかを確認する必要がありました。 そこで必要になるのが 「Ray」 です。 どこのページを見ても「Rayというのは、見えないレーザーみたいなものです」という説明があり「レーザー?レーザー????なんでレーザーなの?」とか考えていました。 しかし動かしてみるとピンときました。設定した長さの棒をだして、それに当たったものを認識するんだな?見えないコリジョンだな?そうなんだな?と。 Rayは見えない物体認識棒 Rayという名前ゆえに「レーザー」と言われるのだと思いますが、僕は「棒」と思ったほうが現実味が合ってしっくりしました。そうして、遠隔のものを認識する、という術を手に入れました。 他オブジェクト内のPublicな変数はどうやって取り出すんだ?? コードを書いていくと、当然この問題にぶつかります。 例えば、Objctive-Cのコードでは、別クラスのPublicな値を手に入れるには EXObject exObject = [オブジェクト呼び出し]; BOOL flag = exObject.flag; というふうに書けばいいのですが、これはUnityだしUnityScript。当然同じように書いてみても取れません。 そもそも「オブジェクトにスクリプトを配置する」という行為自体に理解が追いついていないのです。某書籍では、「static publicにしてアクセスすればいいんだよ」という風に書いてあったけど、それだと明らかに柔軟性に欠ける。なにかいい方法はないのか。 GameObject go = GameObject.Find(“exObject “); EXScript exScript = go.GetCompornent(EXScript); bool flag = exScript.flag; となる事を知る。 スクリプトはいちコンポーネント扱い アプリ開発をしている時、コードこそが本体だった自分としては、コンポーネントの1つとしてScriptがある、という考えにいまいち至らずこの方法を知るまで、static publicで頑張ったり、1つのコードのまとめちゃったりなどをやっていました。 今考えれば馬鹿なことをしていたなあ…。 JavaScriptから、C#のコードにアクセスできるの? 結論から言うと、出来なくないらしいけど僕は出来ませんでした! とある人曰く「Pluginsフォルダを作ってそこに入れれば、アクセスできるよ」という情報をもらいました。 しかし、やってみるとコンソールで赤が出まくり、「あーもうやだ!アクセスする部分はC#で書くわ!」となりました。 その結果、JavaScriptとC#が混同するプロジェクトとなってしまったのがとても残念です…。 現状のものはデモなので、きちんとしたものを作る場合はC#で統一しよう、と固く心に誓っています。 壁はあれども、使いやすいUnity まだまだたくさんあるのですが、強く覚えているものは以上です。 知識のなさゆえにぶつかることも沢山ありますが、Unityは使いやすく、作ってゆくのが楽しい開発環境です。 この僕の転んだ例を見て、スムーズな滑り出しができることを祈ります。
こんにちは、上津原です。 Oculus Riftというヘッドマウントディスプレイがあります。 以前紹介しました。( http://nextdeveloper.hatenablog.com/entry/2013/12/05/102157 ) それを利用し3Dデータで作られた部屋をバーチャルリアリティで内見できるアプリケーション「Room VR」を開発しました。 オプションとして 目線の高さ変更 建物の高さ変更 朝昼夕晩の光さし方の確認 物の移動、配置、削除 などの機能も盛り込んでおり、これを利用することにより、子供目線の確認、モノの搬出入、光のさし具合のシミュレーション、部屋の高さによる風景のシミュレーションなどが可能になります! それを開発するにあたって、Unityや3D完全初心者の私がアプリケーションを開発する間、何を調達して、何を学び、どうすれば作れたかをまとめていきたいと思います。 開発期間は2ヶ月半。前提のスキルはiOSアプリ開発とPhotoshopです。 まずは準備編ということで、環境と必要なもの、参考になるものを挙げていきます。 これからOculusRiftで開発をする人の参考になれば幸いです! 開発環境 実際開発に使った環境は以下です Windows7 OculusRift Unity Pro XboxController(コード付き) HDMI - USB変換ディスプレイアダプタ ひとつひとつについて説明していきます。 Oculus Rift(約¥36,000) https://www.oculusvr.com/order/ 公式サイトで申込みます。$300+送料がかかります。 購入方法は、以下を見ながら買うとわかりやすいです。 http://www.ocufes.jp/2013/10/175/ 私が購入した時は、3週間位で届きましたが、最近はもう少し早く届くようです。 Unity Pro(¥157,500) https://store-jp.unity3d.com/ OculusRiftを購入すると、UnityProの体験版コードがが4ヶ月分ついてきます。 なので、開発を始めるには購入する必要はありません。 体験期間が切れても開発を続ける場合は、Unity Proの購入が必要です。 Xbox360 Controller(¥2,982) http://www.amazon.co.jp/gp/product/B004DL20UU Windowsの場合、OculusのSDKがXbox360コントローラに対応しています。 Macの場合は対応していないので注意が必要です。 Xbox360コントローラを利用する場合、有線のものを買う必要があります。 無線のXbox360コントローラを有線にするようなものが売っていますが、あくまで電池充電器であって、USB接続コードではありません! 実際に間違って買って動かないのを確認しました…。無駄な出費…。 利用の場合は、きちんと有線のものを買いましょう。 HDMI - USB変換ディスプレイアダプタ(¥6,800) http://direct.sanwa.co.jp/ItemPage/500-KC007 DVI端子がなかったため、購入しました。 fpsが落ちるかな?と思ったのですが、今のところ違和感なく動いています。 DVI端子、またはHDMI端子があるPCには必要ありません。 初期費用合計は ¥45,782となりました。(UnityProは購入してません) コントローラもUSB変換も使わないよ、という場合は、Oculusの¥36,000だけになりますね。 言語 経験があったのでJavaScriptを使いました。 ただ、OculusのSDKはC#で書かれているため、途中で「C#で書けばよかったな」と思いました。 JavaScriptでも開発はできますが、C#の理解があったほうがいいには変わりないです。 学習 まずは本で学習し、そこから必要な物をWebで調べながら実装する感じで作りました。 読んだ本 Unity入門 ~高機能ゲームエンジンによるマルチプラットフォーム開発~ http://www.sbcr.jp/products/4797365337.html Unityの基本の動きを確認するためにひと通り動かしました。 参考にしたサイト Unity公式ドキュメント http://japan.unity3d.com/developer/document/ 詳しく内容を知りたい場合やチュートリアルなどお世話になりました。 ActinScript入門Wiki http://www40.atwiki.jp/spellbound/pages/1303.html 検索するとしょっちゅう上に出てくる。 基本的な部分を押さえるのにお世話になりました。 助けてもらったサイト Unityユーザー助け合い所 https://www.facebook.com/groups/unityuserj/ ほんとうにわからない部分はこちらで質問しました。 必ず回答が来るというわけではないですが、助けていただける率は高いと思います。 3Dモデリング 素材では足りないものも多かったため、一文のオブジェクトはモデリングソフトを使って作りました。 メタセコイア http://metaseq.net/jp/ プロ版の体験版にFBX出力があるので、それを利用しモデリング。 素材 開発段階では簡単な素材で作るのが楽で良い感じ。 Sketch Up 3Dギャラリー http://sketchup.google.com/3dwarehouse/ からColladaデータをダウンロードして FBX Converter http://usa.autodesk.com/adsk/servlet/pc/item?siteID=123112&id=10775920 を使ってFBXに変換。 以上が準備したものです。 一気に並べると結構たくさんあるように見えますね。 とりあえずは、Oculusを注文して、届くまでの間にUnityの勉強を…というのがいいと思います。 メタセコイアは、実装が殆ど済んでから最後に触り始めたので、最初の段階では必要ないかと思います。 まずは素材を使って実装をし、そこから思ったものを表現したいときに、メタセコイアなどは触り始めればいいと思います。 さて、これで準備ができたら、次はレッツ実装編です!
大坪と申します。2/27から行われました インタラクション2014 というカンファレンスに参加しました。ここではそのうちのインタラクティブ発表(1日目及び2日目)について「なにがしか書きたくなったもの」について書きます。     ちなみに私は人気のあるデモに並んででも体験する、という忍耐力を持ち合わせないので例えば論文賞を受賞した研究のデモは遠目に観ただけで体験しておりません。結果としてここにも書けないのでした。他の人気があった発表も以下同文です。またひたすら勉強になったものについても書いておりません。発表者については論文のFirst Authorの方のみ挙げております。   --- SenseChairを用いた眠気検出に関する検討:阪大 宮崎さん   椅子にセンサーを仕込んでおいて、ユーザの姿勢変化を検出しそこから眠気を察してやろう、という研究。「椅子にセンサー仕込む研究っていくつもあったような気がするけどどこが新しいですか」と聞いててみればなんと「実際に研究室の学生の椅子に設置し、居眠りしている学生の姿勢をデータとして取得。その上で識別特徴量とかアルゴリズムを設定」とのことでした。   いや、すばらしい。私のようないい加減な人間がこうした研究を行うと   「被験者10名について、”居眠りしたつもりになって”もらいデータを取得しました。判定精度は、、」   とかやりがちです。しかしそれはあくまでもfakeでしかない。私はこのように現実に接地した研究に感動します。でもって気になったので   「学生さん、寝てくれましたか?」   と聞くと「なかなか寝てくれませんでした。一人よく寝る学生がいたので...」と率直に語ってくれたも印象的。   --- 交通事故低減のための運転者状況共有システム :ATR 内海さん   車内にカメラおいて、映像解析結果から運転手の状況を推定しようという研究は多くあります。この研究はそこから一歩進み、例えば   「今ドライバーは眠くなってますよー」   といった情報を車の外に対して提示してやろう、というもの。たとえば車の上に目玉がついていて、それが眠そうな目になるとか。   ユーザにアンケートとったところ、そうしたドライバーの情報について周りと共用することについて結構皆さん抵抗が無い、ということでした。では実際に事故とか起こった時不利な証拠として採用される可能性もあるのではないか、それでも情報を共有したいと思うのか、と聞いた所   「どんな相手と共有するか、というのを細かくアンケートと撮ったところ、警察相手というと受容度が下がった」   とのこと。やはりこうした試みが現実世界にでていくといろいろあるんだろう、と想像しました。   --- マグネイル:爪装着型磁石を用いたインタラクション:お茶の水女子大 門村さん   個人的には一番面白かった発表。女性はネイルアートというかとにかく爪にあれこれつけてデコレーションすることがあります。そのデコレーションに磁石を仕込んでやる。   でもってスマホの地磁気センサーの情報を「ものすごくがんばって」解析処理すると磁石を仕込んだ爪がどのあたりにあるのかがわかる。その情報を使えば、たとえば中指が伸びているか曲がっているかでお絵描きソフトの「描画モード」と「消去モード」を切り替えるなんてことができる、というもの。   何よりも既存のスマホに何も付加する必要がなく、かつ操作側にも(ネイルアートに抵抗がない人であれば)あまり負担をかけることがなく新しい入力を実現しているところがすばらしい。しかし一番感動したのは、学生といっしょに自らつけ爪をつけデモをしていた椎尾教授(50代♂)の姿でした。   --- Do-seiさん: 「やっておいたよ」メッセージの書き置きによる架空エージェントの存在感演出:京都工繊大 髙島さん   人間とコミュニケーションをするエージェントに関する研究は多い。でもたいていの場合それらのエージェントはなんらかの姿を持っている。(男性が作るときは、目の巨大な女性にすることが多かったりする)しかしあえてそうした姿を持たせず、エージェントは相手が帰宅したときに「置き書き」だけを残す。そうして情報を絞ることによりユーザの妄想を誘う、というのが狙いの研究。   私が説明を聞いているとある人が「例えば、夜遅く帰ってきたのだったら”大丈夫?疲れていない”とかメッセージに含めると面白いのではないか」とコメントをしていました。こういう話を聞くと私は意見を述べずにはいられない。   「そうした”状況に応じた人間との対話”は一見面白そうに見えるし、こういうデモの場では受けるのだが、長い間使うとユーザは飽きてしまう。バリエーションがないことがわかるからだ。人間を飽きさせない「自然な会話」というのはそれだけで大変な話。逆にもっと情報を削り、ユーザの妄想をかき立てるほうがいいのではないか。一番怖い物は形のない恐怖だと言う。それは形を持たせないことにより、人間が勝手に妄想を膨らませるからだ。同じように考えると、置書きとして文章を出すより例えば単語だけ提示してユーザに妄想させるのがいいのではないか」   概略このようなことを述べました。発表された方は大変素直かつ柔軟性のある方で、このように見学者が(好き勝手に)真逆の意見を述べていても、きちんと聞いて対応してくれたのが印象的。   --- SoundShare: アドホックな情報共有のためのグルーピングの手法:東京工科大 依田さん   「グループで簡単に写真を共有する」ためにはなんらかの方法で「共有場所」の情報をそれぞれのスマホ(でなくてもいいのだけど)に教える必要がある。実際世の中にはそうしたシステムはいくつもあるのだけどなかなかちゃんと動かない。やれパスワードを忘れた、やれ無線がつながらない、そもそもBluetoothって何のこと、とか。   というわけで一番「原始的」ともいえる「一台のスマホから共有場所の情報を乗せた音を出す。他のスマホはその音を解析することで共有場所の情報を得る」というもの。ピーヒャラーと鳴る音を聞いていると、昔なつかし「モデムの音」を思いだす。そう感想を述べると、この研究の発表者自身はその音を聞いた事は無いが、同じような感想を述べた「おじさん」がいたとのことでした。   いろいろ突っ込みどころはあるかもしれませんが、着眼点は素晴らしい。これだけWebサービスだなんだ、があるにも関わらず「集まったグループで写真を簡単に共有する」というのは未だに解決されていない問題です。解決策は世の中に山ほど提案されているがどれも肝心な時に動作しない。結局現実世界で一番確実に動くのは「原始的な手法」ではなかろうか。確かに電波の悪い場所であっても、スマホである限り「音を出し、かつそれを取得する」ことは可能なはず。またQRコードを使うのに比べても、一度に複数の端末に情報を転送できる、というメリットがあります。   説明を聞いているうち、3年前を思い出しました。インタラクション2011の二日目、東日本大震災の時に無線接続のインターネット、電話は全く役に立たなくなりました。それまでカンファレンスで聞いていた「素晴らしいシステム」はもちろん、数々のWebサービスは使えなくなり結局「時代遅れ」の公衆電話に長い列ができたわけです。私が新しいシステムに対して「現実世界に接地している」ことを強く意識するようになったのはあの時から,,というのは嘘です。   --- Pay4Say: 貨幣制度を導入したビデオ会議システム:北陸先端大 永井 さん   会議で「声が大きい人だけが発言する」という現象は大学でも会社でも起こりうるわけです。でもってそれを平準化するため   ・参加者は発言コインを持っている   ・実際に発言すると、発言コインが、誰かに贈与できるコインに変換される   ・贈与コインは(匿名で)誰にでもあげることができる。贈与コインも発言によって消費される   というシステム。面白いと思ったのは   「声だけ大きくて内容がからっぽの発言をする人」   が、このシステムを使うと可視化されるのではないか、と思ったから。上司とか教授がこのシステムであっというまに発言を封じられたりすると、結構複雑な人間関係を浮き彫りにさせられるような気がします。   そういう具体例はないのか?と尋ねたところ、あまりない、との返答。おそらくこの研究をされた方の研究室には理性的な人がそろっているのでしょう。   --- CCC: 振動機能を応用した携帯端末での個人認証における覗き見攻撃対策手法の提案:電通大石塚さん     携帯端末のパスワード入力は覗き見されると容易にばれてしまう。それを防ぐため、端末の振動機能を使おうという試み。   聞いていて   「そういえば、サッカーワールドカップの予選組み合わせ抽選も全世界にTV中継される中で行われるけど、温度を使ったり、振動を使ったりいろいろ不正な行為があると聞いた。それと通じるものがあるなあ」   と思いました。逆に「サッカーワールドカップ抽選から発想を得た”覗き見されない”認証手段」なんて研究も有りかもしれません。   --- というわけで、最近とみにインタラクティブ発表が好きな私です。なんといっても研究を実際に行った人と様々な議論ができるのがうれしい。などと喜んでいると   「踊るなんとかに観るなんとか」   という言葉が頭をよぎる。というわけで次にインタラクションに参加することがあれば、何か持って行く可能性がないとは必ずしも言えないのでした(弱気)
大坪と申します。人前でプレゼンテーションをする機会というのは、私が若いころに比べてずいぶん増えたように思います。 でもって「プレゼンのコツ」なるものも世の中に山ほど出回っている。フォントは24ポイントだか40ポイントだとか、あるいは箇条書きにしろとか、スライドのデザインはスッキリしろとか。 しかし 私見ではそれらは末端の些事にすぎない。フォントを40ポイントで統一しようが、高い金を払ってプロのデザイナーに「スライド」を作ってもらおうが、プレゼンテーションの基本原則を考えていないプレゼンは駄目プレゼンです。(きっぱり) PowerPointだかそれに類するソフトウェアを立ち上げる前に、一歩引いて考えましょう。そもそもプレゼンテーションは何のためにするのでしょうか?ネットで検索すると、 良い言葉にいくつも出会えます がここでは以下の定義を使います。 定義:プレゼンの目的は「売り込み」である つまり聴衆が「あなたが売りたい物について話しを聞く気にさせる」のがプレゼンの目的です。ここで「売る」というのは別に金銭の移動を意味しているわけではない。「これはよい」「面白い」と思ってもらえれば、売り込みは成功です。しかしここでプレゼンと似て非なる「説明」というものの存在について考えなくてはならない。 「説明」とは何か。それは既に「買う気になっている相手」に細かい条件等を話すことです。主たる目標は情報を伝えることであり、「売り込み」ではない。この区別は時として曖昧になりがちであり、ほとんどの場合は両者が混じっています。しかしこの定義を疎かにすると後の「良いプレゼンを行うための行動」が全て的はずれなものになってしまう。 貴様ごときチンピラエンジニアが何を言うか、と思われる方もいると思うので別の方の言葉を引用しましょう。 これらの事の本質は実にシンプルで「自分のいいたいことが相手に伝わり、共感してもらえなければ、そもそもプレゼンする価値がない」という至極当たり前のことである。その観点にたてば、それまでの自分の説明というのはおおむね「聞いて理解しようと努力してくれる人にたいして、各種データを提供していただけ」といことを、その後ベインでいろいろなプロジェクトに関わり、たくさんのプレゼンをこなす中で、徐々に理解するにいたるのであった。 スティブージョブス驚異のプレゼン P386 解説外村仁氏 上記引用文中で「自分のいいたいことが相手に伝わり、共感してもらえる」というのが「売り込み」であり、「聞いて理解しようと努力してくれる人にたいして、各種データを提供する」のが「説明」です。 最近聴力がだいぶ衰えてきたので、異論は聞かなかったことにし上記の定義を元に考えましょう。例えばあなたが感動的なスライドの束を作り上げたとして、それをどこかに置いておけば皆がそれを読み、あなたのところに「もっと詳しい話を聞かせてほしい」と連絡してくるでしょうか? 例えば ポアンカレ予想を証明したペレルマンの論文 はそうしたものだったかもしれません。彼はインターネットサイトに論文を投稿するだけで、驚くほどの「売り込み」に成功したのです。しかしもしあなたがポアンカレ予想よりは多くの人に着目されていない問題に対して、ペレルマンの論文ほど画期的でない事柄を述べようとするのなら、別の方法を考える必要がある。 別の方法とはなにか?プレゼンテーションです。プレゼンテーションを行う機会を与えられた、ということは、単に「だれでもアクセスできる場所に文章を置いた」のとは違い、忙しい聴衆があなたの「売り込み」を聞くことに貴重な時間をコミットしてくれた、ということです。 であれば プレゼンターは「自分の言葉で語りかける」必要がある。人間に対して売り込みができるのは人間だけです。紙や画面に映しだされた文字ではない。 つまり プレゼンテーションは「プレゼンターが聴衆に語りかける場」なのです。聴衆が配布された印刷物を読む場でも、画面に映しだされた文字を読む場でも、ましてやその文字を朗読するのを聞かされる場所でもない。聴衆は「プレゼンターから言葉を聴くために」きているのです。 こう考えると次の事がわかります。 プレゼンは朗読会ではない=「しゃべる内容」を画面に書いてしまうこと、これはプレゼンテーションを行う上で根本的な間違い 大事なことなのでもう一度書きます。プレゼンは朗読会ではない。あなたがいかに美声の持ち主であり、すばらしい声の表現力を持っていたとしても、画面にかかれた文字を朗読することで「なるほど、これは素晴らしい内容だ」と考える聴衆はいません。読んでわかる内容なら、聴衆は時間をコミットする必要はない。資料を送ってもらう(そしてほとんどの場合そのまま削除する)だけで十分です。 たとえばよく見かけるこんな「スライド」。これは「売り込み」という観点からは間違っている。 なぜ間違いか?このスライドがあればプレゼンターは要らないからです。 いや、そういっても書いておけば何かいいことあるかもしれない、と考える人もいるでしょうがそうではない。「しゃべる内容を書いた画面」を目にした途端聴衆はプレゼンターの言葉を聞かず自分勝手なペースでその文字を読み出します。さらに悪いことに、ほとんどの人はプレゼンターが朗読するより早く文字を読み終えますから、 「もうわかったからさっさと次の話しを聞かせてくれ」 と内心いらいらしながら朗読が終わるのをまつことになる。 このことから、私は次のように主張したい。 「スライド」を印刷して配布したり、SlideShareにアップロードしてすむようなら、人前でしゃべる資格はない これについては少し補足が必要です。喋った後に「より細かい資料」を公開して読んでもらうのはとてもいいことだと思います。しかしそれとてプレゼンという売り込みが成功してからの話。つまり「プレゼンテーションを印刷」とか「プレゼンテーションをアップロード」というのは(プレゼンテーションの音声も合わせて記録した動画、あるいはそれに類する形式 *1 でない限りそもそも意味を持たない行為なのです。 さらに次の事実も合わせて考える必要がある。 事実:聴衆がプレゼンの間に受け取れる情報量は信じられないほど少ない まずプレゼンの定義を思い出しましょう。プレゼンを行う時点では、そもそも聴衆はあなたが売り込もうとする内容について「ふーん」くらいの期待しか持っていないわけです。よくてどんよりした目を前方に向けている、悪ければ寝ているかもしれません。 そういう姿勢でいる相手がどれくらいの情報量を受け取れるか、というのはお昼ごはんの後の講義で、例えば15分の間にどれくらい先生の話が頭に残っていたかを思い出せばぼんやり想像できると思います。 あるいはこう考えてもいいかもしれません。私が昨年参加したWISSというワークショップでは、六頁の論文を書きます。プレゼンの時間は15分。この論文を最初から朗読するとどれくらい時間がかかるでしょうか?一度やったことがあるのですが、初めて聴く聴衆が理解できるようにゆっくり読み上げると軽く2倍の時間がかかります。つまりあなたが文章で表現したい内容に対して最高でも半分しかしゃべることができない。聴衆の記憶に残り、興味を持ってもらうためにはそれ以上に内容を削らなければならない。 ですから 「プレゼンテーションの準備」をする際にまず考えるべきなのは 自分は何を売り込みたいのか それをわかってもらうために、エッセンスをどう抜き出し、どのように語ればよいか これらをきちんと考えることが第一に必要。それを飛ばして「プレゼンの準備をしよう」といって最初にPowerPointを立ち上げたとすれば、その時点で失格。そもそも自分が何を売り込みたいのかわかっていないのに、「スライド」をいくら作っても、誰も聞いてはくれません。 ここまで書いたことを踏まえ、最後にやりがちな間違いについて書いておきます。 自分がやったことを順序良く説明するのは、プレゼンテーションではない。 それは「説明」です。もっとも相手が「そうした説明」を期待している場合は別ですが。いや、俺(もしくは私)はこんなに苦労して結果をだしたのだから、その過程を克明に語る資格があるし是非聞いてもらいたい、というなどと考えたとすれば、それは間違っています。以下の文章を読みましょう。 ──ではよくある“ダメなプレゼン”はなんでしょう? 自分のことばかりを語るプレゼンですね。最初に触れた通り、自分が話したいことではなく「相手が聞きたいこと」を心に響くストーリーに仕立てて話すことがいちばん重要なことです。 引用元: 五輪招致戦略コンサルタント、ニック・バーリー:「世界を動かすプレゼン力を伝授します」 « WIRED.jp あなたがものすごい有名人であるか、あるいは観客全員が何らかの理由によりあなたに恋しているのでなければ、誰も「あなたがどんな人で何を考えているか」なんかに興味を持っていません。観客は自分が聞きたい事を聴くために、そうした「自己中心的」な理由のためにそこにいるのです。相手が何を聞きたいと思っているのか。それを考えることが第一歩。 以上が私が考えるプレゼンテーションの「原則」です。原則は原則であって、全ての場合の真理ではない。Apple原理主義者といえどもWindows上でC#でプログラムを書かざるを得ないこともある。上司の求めに応じ、PowerPointで作った「説明資料」を読み上げなくてはならない場合もあるでしょう。 しかしながら 原則とその応用形を混同してはならない。プレゼンテーションの原型はプレゼンターが聴衆にしゃべりかけ、どうしても必要な場合だけビジュアルエイドと呼ばれる図表を用いるものでした。しかしソフトウェアを用いてスライドを作ることが容易に行われるようになった今、プレゼンテーションの主役がプレゼンターではなく「スライド」になっているのではないか。 「明日のプレゼンの用意全然できてないよ。早くスライド作らなくちゃ」 という言葉を聞く事は多い。この言葉は暗黙のうちに「プレゼン=スライド」という等式を想定しているかのようです。しかし私見ではこんなことを言っている時点でプレゼンは聞くに値しないものになっている可能性が高い。 私はPowerPointとかの「スライド」作成ソフトの使用を否定するものではありませんが、それらがもたらす問題についてはよく考える必要があります。ではそれは何なのか、については以下次号(次号があればの話ですが) *1 : 任天堂がサイトに掲載している 決算説明 がこの例です
ネクストでレコメンドエンジン開発をしてる古川です。 rubyで、ファイルを読み込んで加工して別のファイルに出力というプログラムをよく書きます。 最近、rspec でテストを書くようになったのですが、beforeでテスト入力ファイルを作成し、 after で作成したテスト入力ファイル、テスト出力ファイルを削除、ということをしていました。 とりあえずは、これで問題なかったのですが、同時実行時や、実行時パス、パーミッションなど、 今後いろいろ問題になりそうで、いやだなと思っていたところ、 Stack Overflow に、 まさに、それがやりたかったんだよ!という 記事 を見つけました。 StringIOを使えばよかったのですね。 記事 は、読み込みテストだけでしたので、書き込みテストも追加してみました。 テスト対象クラス my_file_io.rb class MyFileIo def run (path_src, path_dst) x = load (path_src) y = transform(x) save(y, path_dst) end def load (path) fp = :: File .open(path, ' r ' ) data = [] fp.each do | line | line.chop! data.push(line.split( "\t" )) end fp.close data end def transform (x) x.map {| t | t.map {| s | s.gsub( / a / , ' b ' )}} end def save (y, path) fp = :: File .open(path, ' w ' ) y.each do | x | fp.write(x.join( "\t" ) + "\n" ) end fp.close end end spec ファイル my_file_io_spec.rb describe MyFileIo do let( :target ) { MyFileIo .new } it ' 元ファイルのa が bに置き換わったファイルが作成されること ' do path_src = ' src.tsv ' path_dst = ' dst.tsv ' data_src = [ [ ' a11 ' , ' a12 ' ], [ ' a21 ' , ' a22 ' ] ] data_dst = [ [ ' b11 ' , ' b12 ' ], [ ' b21 ' , ' b22 ' ] ] contents_src_file = (data_src.map {| x | x.join( "\t" )}).join( "\n" ) + "\n" :: File .stub( :open ) .with(path_src, ' r ' ) .and_return(:: StringIO .new((contents_src_file), ' r ' )) file_dst = :: StringIO .new( '' , ' w ' ) :: File .stub( :open ) .with(path_dst, ' w ' ) .and_return(file_dst) ans = (data_dst.map {| x | x.join( "\t" )}).join( "\n" ) + "\n" target.run(path_src, path_dst) file_dst.string.should eq(ans) end end Stack Overflow いつも、お世話になってます。
Apple原理主義者の大坪です。何故Apple原理主義者がC#を使うか?私は狂信的なApple原理主義者ですが、現実主義者でもあるのです。必要があればなんでも使ってやろうじゃないの。 とはいえ 最近昔を思い返すことが多い。当時は新しい環境に移る時まず本を買ったものです。しかし最近はGoogle先生にお伺いをたてればあれこれ情報が手に入る。とはいえ新しい環境に移るときは 「そもそも何のキーワードで探せばよいのか」 という問題にぶつかる。WindowsのPC用アプリケーションってなんて呼ばれているのか。(Macなら"Mac OS X Application"ですむのですが) というレベルなので、今回書く内容は多分最適とか正解からはほど遠い物で、「今とりあえずこれで動いている」というものです。おまけにあれやこれやの理由から動くプロジェクトではなく、必要部分のソースだけを出しますがご容赦ください。 さて 試行錯誤の末まずたどり着いたのが"WinForm"なるキーワードです。しばらくそれを使ってあれこれやっていたのですが、どうも様子がおかしい。調べてみると最近はそれが WPF なるものに変わったらしい。でもって「ザムル」とかいろいろ恐ろしい言葉が並ぶ訳です。いつのまにかGUIはXMLで定義し、その背後のコードと分離するやり方が主流になったのだな、、などとXCodeで作る際にも Interface Builder( 年がばれるか?)を使わない人間としては感慨に耽るのでした。 などとあれこれ言っている場合ではない。画面がなんとかできると、今度はC++で開発されたDLLライブラリの中の関数をC#からコールし、シリアル通信を行う必要があるのです。その部分のコード(一部)を記事の末尾に示します。ちなみにここに示したコードは「駄目なコードでもないよりはマシ。少なくとも出発点として使ってもらえるだろう」ポリシーの元公開しておりますので、左様ご承知頂きたく。 このコードは、ソーバル社のHRW-1000というRFIDリーダーからカードの情報を読み取るためのものです。ドライバはTRW社のサイトからダウンロードできます。ここからプロトコル仕様に従って、ごりごり通信を行うこともできるのでしょうか、せっかくWindowsで開発しているので、ソーバル社から提供されているDLLライブラリを利用して通信します。 やりたいことはなにか?言葉で書けば ・デバイスをオープンしてCOMポートで通信できるようにする ・「カードを読み取ってね」というコマンドをbyte列にしてリーダーに送信する ・読み取った結果を、byte列としてリーダーから受信する。 明確だし、簡単に聞こえますね。しかしこうしたハードウェアに近いところの処理というのは、その環境特有のあれこれをしないと動いてくれない。Apple原理主義者がつまづいた点を以下に列挙します。 C#からC++用のDLLを呼ぶ時には、DLLImportというものを使わなくてはならない。 それまでもC#用のDLLを使う事があったのですが「参照の追加」をすればなんとかなりました。しかしC++用のDLLではそれだけではだめ。DLLImportで場所を指定する必要があります。 DllimportでGoogle先生にお伺いを立てると、シンプルな例がいくつもでてきます。そうかそうか、と 素直に DLLImport("rfidsobal.dll); と書いたのはだめで、ここに書いたように"CallingConvention = CallingConvention.Cdecl"とパラメータを指定する必要があるようです。なぜかというと、このDLLが「アンマネージドコード」と呼ばれるものであるため。この場合はWin32.APIを用いているようです。 ライブラリとのパラメータ変換 次の問題はパラーメータの型変換です。メインのプログラムはC#で、DLLはC++用のもの。そりゃ言語も違うから型も違うだろう。ではどうしよう、とGoogle先生に聞いてみると こんなページ がヒットする。とても解りやすくかかれているのですが、ここだけ観ていればOKというほど話はストレートにいかない。 今回使うAPIでは受信データをchar にいれてくれることになっています。なるほど、ではchar は、、、そうかSystem.Text.StringBuilderにすればいいんだ、とやってみると何故か最初の一文字しか受信できない。これは困った。 後から考えれば当たり前の話で、APIではchar*になってますけど、具体的にはbyteでデータをつめて返してくれるわけです。それはもちろん文字列ではない。しかしStringBuilderで受けると「ここに入ってるのは、当然文字コードなんだよねー」と「解釈」されてしまいます。結果として、どうやっても最初の一文字しか読み取れない。(2byte目にnullがあったんだと思います) ではどうするか、というわけでまたあちこちさがしてたどり着いたのがprivate void checkResult()に書いたような方法です。 多分これはbyte配列をそのまま受け取るようなことをしているのだと。これでめでたくデータ受信ができました。 では私はなぜApple原理主義者としての矜持を捨ててまでWindowsでプログラムを書き、RFIDリーダーと格闘しているか。その理由についてはいつかお話する機会もあろうかとおもいます。
ネクストでレコメンドエンジン開発をしてる古川です。 前回 は、solr で独自基準ソートを実現する方法として、「1.既存のfunction query を組み合わせで実現する方法」を紹介しましたので、今回は、「2. 独自のfunction query 作成して実現する方法」を紹介したいと思います。 solr のソースコード確認 まずは、solrのfunction queryがどのように実装されているか、recip関数を対象に見てみます。 solr のソースコードを取得して、適当なディレクトリに展開します。 wget http://ftp.yz.yamagata-u.ac.jp/pub/network/apache/lucene/solr/4.6.1/solr-4.6.1-src.tgz tar xvzf solr-4.6.1-src.tgz recip関数 の説明を読むと、recip(x,m,a,b) のようにクエリで指定し、xはドキュメントのフィールド名か、もしくは数値を返す関数、その他の、m、a、bは、数値定数であると記述されています。 solr-4.6.1/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java のrecip関数に関連する部分 151 addParser("recip", new ValueSourceParser() { 152 @Override 153 public ValueSource parse(FunctionQParser fp) throws SyntaxError { 154 ValueSource source = fp.parseValueSource(); 155 float m = fp.parseFloat(); 156 float a = fp.parseFloat(); 157 float b = fp.parseFloat(); 158 return new ReciprocalFloatFunction(source, m, a, b); 159 } 160 }); を見ると、ドキュメントに依存するフィールド値をValueSourceクラスの変数 sourceとして、ドキュメントに依存しないその他は、float型の変数m、a、b として解釈し、それを引数にReciproacalFloatfunction クラスを生成して返していることが分かります。 ReciproacalFloatfunctionの中身をみると、意外と簡単なコードであることが分かります。 solr-4.6.1/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/ReciprocalFloatFunction.java これらをベースにして作成していけばよさそうです。 plugin jar ファイル作成 ReciprocalFloatFunction.java をまねして、myfunc(x,y,a,b,c,d) という、ドキュメントフィールド値x, y とその他、4つの数値定数を入力とし、a x x + b x y + c y y + d x + e y + f の計算結果を返す独自クラスを実装してみたのが、以下のファイルです。 MyFloatFunction.java package jp.co.homes.functionquery; import org.apache.lucene.index.AtomicReaderContext; import org.apache.lucene.queries.function.FunctionValues; import org.apache.lucene.queries.function.ValueSource; import org.apache.lucene.queries.function.docvalues.FloatDocValues; import org.apache.lucene.search.IndexSearcher; import java.io.IOException; import java.util.Map; public class MyFloatFunction extends ValueSource { protected final ValueSource x; protected final ValueSource y; protected final float a; protected final float b; protected final float c; protected final float d; protected final float e; protected final float f; /** * f(x,y,a,b,c,d,e,f) = a*x*x + b*x*y + c*y*y + d*x + e*y + f */ public MyFloatFunction(ValueSource x, ValueSource y, float a, float b, float c, float d, float e, float f) { this .x = x; this .y = y; this .a = a; this .b = b; this .c = c; this .d = d; this .e = e; this .f = f; } @Override public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException { final FunctionValues xVals = x.getValues(context, readerContext); final FunctionValues yVals = y.getValues(context, readerContext); return new FloatDocValues( this ) { @Override public float floatVal( int doc) { float x = xVals.floatVal(doc); float y = yVals.floatVal(doc); return a*x*x + b*x*y + c*y*y + d*x + e*y + f; } @Override public String toString( int doc) { String xd = xVals.toString(doc); String yd = yVals.toString(doc); return Float.toString(a) + "*" + xd + "*" + xd + '+' + Float.toString(b) + "*" + xd + "*" + yd + '+' + Float.toString(c) + "*" + yd + "*" + yd + '+' + Float.toString(d) + "*" + xd + '+' + Float.toString(e) + "*" + yd + '+' + Float.toString(f); } }; } @Override public int hashCode() { int h = Float.floatToIntBits(a) + Float.floatToIntBits(b) + Float.floatToIntBits(c) + Float.floatToIntBits(d) + Float.floatToIntBits(e) + Float.floatToIntBits(f); h ^= (h << 13 ) | (h >>> 20 ); return h + (Float.floatToIntBits(b)) + x.hashCode() + y.hashCode(); } @Override public boolean equals(Object o) { if (MyFloatFunction. class != o.getClass()) return false ; MyFloatFunction other = (MyFloatFunction)o; return this .a == other.a && this .b == other.b && this .c == other.c && this .d == other.d && this .e == other.e && this .f == other.f && this .x.equals(other.x) && this .y.equals(other.y); } } 次に、検索クエリにmyfunc(x,y,a,b,c,d,e,f)という文字列(x,yは任意の数値フィールド名、a-f は数値定数)が 与えられた場合に、引数を解釈してMyFloatFunctionクラスを作成して返すクラスを実装します。 HomesValueSourceParser.java package jp.co.homes.functionquery; import org.apache.lucene.queries.function.ValueSource; import org.apache.solr.common.util.NamedList; import org.apache.solr.search.SyntaxError; import org.apache.solr.search.FunctionQParser; import org.apache.solr.search.ValueSourceParser; public class HomesValueSourceParser extends ValueSourceParser { @Override public void init(NamedList namedList) { } @Override public ValueSource parse(FunctionQParser fp) throws SyntaxError { ValueSource x = fp.parseValueSource(); ValueSource y = fp.parseValueSource(); float a = fp.parseFloat(); float b = fp.parseFloat(); float c = fp.parseFloat(); float d = fp.parseFloat(); float e = fp.parseFloat(); float f = fp.parseFloat(); return new MyFloatFunction(x, y, a, b, c, d, e, f); } } この二つのファイルを、前回インストールしたsolrのディレクトリ、以下のjarファイル solr-4.6.1/example/solr-webapp/webapp/WEB-INF/lucene-core-4.6.1.jar solr-4.6.1/example/solr-webapp/webapp/WEB-INF/lucene-queries-4.6.1.jar solr-4.6.1/example/solr-webapp/webapp/WEB-INF/lucene-queryparser-4.6.1.jar solr-4.6.1/example/solr-webapp/webapp/WEB-INF/solr-core-4.6.1.jar solr-4.6.1/example/solr-webapp/webapp/WEB-INF/solr-solrj-4.6.1.jar にpathに通してコンパイルして、homes-function-query.jar というjar ファイルを作成します。 設定 前回設定したsolrで、このプラグインを使えるようにします。 まず、collection1フォルダの下にlibディレクトリを作成し、その下に、作成したjar ファイルをコピーします。 mkdir solr-4.6.1/example/solr/collection1/lib cp homes-function-query-plugin.jar solr-4.6.1/example/solr/collection1/lib 次に、 solr-4.6.1/example/solr/collection1/conf/solrconfig.xml の60行目に以下のような二行を追加します。 <lib dir = "./lib" /> <valueSourceParser name = "myfunc" class = "jp.co.homes.functionquery.HomesValueSourceParser" /> 動作確認 solr を起動します。 cd ./solr-4.6.1/example java -jar start.jar & 新しく作成したmyfunc関数を指定したクエリを実行してみます。 http://localhost:8983/solr/collection1/select?q=*:*&fl=x,y,myfunc(x,y,1,2,3,4,5,6) 実行結果 <result name = "response" numFound = "6" start = "0" > <doc> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "myfunc(x,y,1,2,3,4,5,6)" > 87.0 </float> </doc> <doc> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "myfunc(x,y,1,2,3,4,5,6)" > 138.0 </float> </doc> <doc> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <float name = "myfunc(x,y,1,2,3,4,5,6)" > 201.0 </float> </doc> <doc> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "myfunc(x,y,1,2,3,4,5,6)" > 87.0 </float> </doc> <doc> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "myfunc(x,y,1,2,3,4,5,6)" > 138.0 </float> </doc> <doc> <float name = "y" > 6.0 </float> <float name = "myfunc(x,y,1,2,3,4,5,6)" > 144.0 </float> </doc> </result> 正しく計算できているようです。 最後のx値は欠損しているため、xが0として扱われています。フィールド値が欠損している場合に0と扱いたくない場合には、 if (xVals.exists(doc) { ... } のようにして欠損値の場合の処理を追加する必要があります。 速度比較 function query の組み合わせと、独自 function query でどの程度速度に差が出てくるか、簡単な検証をしてみました。 データ量が少ないと差が出てこないので、100万件のデータを追加後、キャッシュが影響しないよう、数値定数を変更しながら、以下クエリのQTimeの5回平均を計算してみました。 既存function queryの組み合わせ http://localhost:8983/solr/collection1/select?q=*:*&fl=x,y&sort=sum(product(pow(x,2),1),product(product(x,y),2), product(pow(y,2),3),product(x,4),product(y,5),6) desc 独自function query http://localhost:8983/solr/collection1/select?q=*:*&sort=myfunc(x,y,1,2,3,4,5,6) desc 結果 function query 組み合わせ 平均QTime 201ms 独自 function query 平均QTime 19ms myfuncの方が、相当高速であることが分かります。function queryの組み合わせでは、同じフィールドに 何度もアクセスが発生してしまうのに対して、独自 function query の方は1回しか発生しないなど、 データアクセスが効率化されているのが原因と思われます。 まとめ 思ったよりも簡単に、独自のfunction query を作成することができました。 この方法の場合、solrのqueryに、そのまま埋め込んで使えるので応用範囲が広いのがポイントで、 大抵の用途には、これで十分ではないかと思います。 次回、「 独自のsearch component を作成して実現 」 に続きます。
こんにちわ、社内でアジャイル推進をしている非エンジニアな鈴木です。 2014/2/19 第1回「Scrum Masters Night」に参加してきました。 Regional Scrum Gathering Tokyo 2014での出逢い、そして参加への決意 招待講演「 失敗体験から学んだスクラムの本質 」で知花さんの講演を拝聴し、スクラム導入時のあるあるを共感。そして、講演の最後で告知のあったスクラムマスターの集いがついに行われました。 ただ私は基本的に人見知りなので、参加どうしようと悩んでいました。 第一回目だし雰囲気わからないし、私なんかが参加していいのか?お話しできるかなー? しかし、他社の方と、しかも現場の方の生々しいお話しが出来る場はなかなかないっ! しかもタダで! これは頑張らねば私、とひとり参加をしてきました。 ディスカッションのテーマに選ばれる さすがスクラムマスターの集い、ただ一方的にお話しを聞くのではありません。 自らディスカッションしたいテーマを発表して、投票してテーマに分かれてディスカッションという形式で行われました。 しかも、テーマ発表して選ばれたらファシリテーターをしなければならないという、またまた小心者の私にはドキドキな状態。 しかし、語りたい! もういっちょ頑張りました私。 そして、数あるテーマの中から発案したテーマが選ばれてしまいました。 ・スプリントゴールってどんなのものを設定してる? 他にも、「アジャイルメトリクス」「ざんねんスクラム」「スクラムやって○○できました自慢」などの気になるテーマに後ろ髪を引かれつつ、 自らテーブルに立ちました。 正直どうしようと思っていたんのですが、普段から話し合い・調和ということに慣れている参加者の方たち。 話しが途切れることなく、用意されていた1時間はあっという間に過ぎてしまいました。 スプリントゴールってどんなのものを設定してる?の解 そもそも、このテーマを取り上げて欲しいと思った理由が「スプリントゴール」って何という疑問。 スクラムガイド2013 ではデイリースクラムでは「スプリントゴールを達成するために」3つの質問をしましょうと再構成されました。 重要なのかな?ストーリーじゃだめなのかな? スプリントゴールを設定している方に、どんなゴールを設定しているか教えて欲しいというものでした。 ディスカッションの内容は、 みなさんが設定しているスプリントゴールについて そもそもゴールいるのかな? ストーリー(プロダクトバックログアイテム)でいいんじゃない? なんのために必要なの? 毎回変えるの? 誰が決めてる?(プロダクトオーナー?スクラムマスター?開発チーム?) どうやって決めている? スクラムガイドを朗読してみる などなど。 ディスカッションの中で、明確な解というものは出てきませんでしたが、 スクラムガイド2013には、 スプリントゴールはスプリントの目標セットであり、プロダクトバックログの実装によって実現するものである。 これは開発チームがインクリメントを構築する理由を知る指針となる。スプリントゴールはスプリントプランニングで作成する。 スプリントゴールを設定することで、開発チームがスプリント終了までに実装する機能を柔軟にできる。 選択したプロダクトバックログアイテムは、一貫性のある機能として届けられる。それがスプリントゴールになることもある。 スプリントゴールがあれば、開発チームは一致団結して作業ができる。 とあるように、チームがスプリントで達成したいことを意識合わせするのに必要だなーと思いました。 どんなゴールがよいか、誰が設定するのか、どうやって決めるかは、それこそチームで考えていかなくてはならないことなのだと思いました。 感謝を伝える Just Say Thanks とても楽しいイベントでした。 このような機会を設けてくださり、しかも懇親会のスポンサーまでしてくださった主催者のみなさま。 ディスカッションで熱く語ってくださった参加者のみなさま。 本当にありがとうございました。 また、3月に開催されるとのことで楽しみにしています。 次回は社内のメンバーと一緒に参加させていただきたいと思います。
大坪と申します。先日見つけた動画の内容が興味深かったので、自分が理解できたところだけ紹介します。 Airbnb Design Talk with Braden Kowitz 12.12.12 ... スピーカーはBraden Kowitz氏。現在はGoogle Ventureのデザインパートナーであり、以前はGMail, Google Spreadsheetsのデザインにも携わったとのこと。このプレゼンテーションの中でKowitz氏は「デザインプロセスにおいて、失敗することの重要性」を説いています。 プレゼンの最初に「失敗したデザインの例」として悪名高いiMacの円形マウスの写真を出します。確かに人目をひくデザインをしていますが、「そもそもどちらが上なのか」わからず困った経験をしたのは私だけではないと信じたい。Kowitz氏の表現を借りれば 「カーソルを上に動かそうとマウスを操作すると、カーソルはとんでもない方向に飛んで行く」 製品でした。実際この後にAppleがデザインしたマウスは、仮に丸みを帯びた形であっても持っただけで方向がわかるようになっています。つまりこれは明らかな「失敗したデザイン」だったわけです。(私はApple原理主義者ですが、このことを認めるくらいの柔軟性は持ち合わせています) さてKowitz氏が最初に取り組んだデザインの仕事は失敗に終わったそうです。彼は一人でコンセプトをつめ、「まだ人に見せられる段階じゃない」と途中のフィードバックも拒み、そして3ヶ月後に「これが成果です」とやったところ結果を誰も理解できなかった。なぜこのようなことが起こるのか? 氏は"Design Blindness”という言葉を用いて理由を説明します。デザインを行う人間は「これは行ける」というある種の確信をもって作業を始めるわけですが、その瞬間から自分のデザインを客観視することができなくなる。それ故Critique Early & Often:早い段階から、できるだけ頻繁に「批評」してもらうことが必要になる、と。 しかし自分の仕事について「批評を受ける」ことは勇気のいることです。特に「まだできていない」作品に対してあれこれ文句を付けられるのは楽しくない。しかし良いデザインをするためには、デザイナーは「失敗」に対してオープンな姿勢を取る必要がある。ここから氏は3つのトピックについて語ります。 まず「楽器の練習」 ピアノに関して本を何冊か読めば、カーネギーホールでピアノを演奏できるか、といえばそんなことはない。もちろん最初にインストラクションは受けるのですが、自分でやってみると指はうまく動かない。何度も繰り返しているうちにようやく目標としているレベルの演奏ができる。このプロセスは「ピアノの失敗」ではなく「ピアノの練習」と呼ばれます。 それ故「デザインの失敗」ではなく「デザインの練習(Practice)」と呼ぶべきではないかと氏は提案します。「失敗」は学ぶことの一部であり、それ無しに上達-あるいはデザインが良くなることはありえない、と。 2番めは「軍艦」。軍艦がどうしたって? 氏が取り上げたのはGmail。Gmailは軍艦のように複雑で巨大なプログラムです。この画面にChat機能を入れようとした際にどんな「デザインの失敗」があったか。最初のデザインはGoogle社内ではとても好評。次に「普通の人」にテストしてもらった時何が起こったか?テストを行う側が最初のメッセージを送ったところ、被験者は 「どうやったら入力した文字が送信されるのかわからず、返答の文字をタイプした後固まってしまった」 そうです。「慣れれば」エンターキー(リターンキー)を押した瞬間文字が送信される、という方法は効率がよいのですが、初めて使う人間にとってみると「送信ボタンはどこ?」となってしまう。よし。では「エンターキーを押すとメッセージが送信されます」という表示をつけよう。 改良されたデザインでテストを行ったところ、結果はまた「失敗」でした。チャットで呼びかけがあったときにポップアップウィンドウを開くようにしたのですが、ユーザはそれを「またウザい広告か」と内容を確認せず反射的にウィンドウを閉じてしまう。 Googleのデザイナーが間抜けばかりということはまずありえない。ではなぜ「良いデザイナー」が取り組んでいながら、これほど失敗を繰り返すのか。氏が得た結論は「デザイナーは普通ではない」から。 ここで氏は「この中でスマートフォン2つ持っている人?」と呼びかけます。おそらく会場の何人かが手を挙げたらしい。しかしスマートフォン所有率が5割とか6割とか言っているときに2台もっているなんてのは明らかに「おかしい」わけです。しかしその「おかしいデザイナー」は「普通の人」の感じ方、見方を推し量らなくてはならない。 つまりデザインというのは「 軍艦ゲーム 」(懐かしい!)のようなもの。D-3とか座標を言うと相手は「おっと命中だ」とか教えてはくれる。しかし結局相手の軍艦がどこにあるかは分からない。つまりデザインにおいて「失敗」は避けがたい要素なわけです。 3番目に挙げられたのは「ユーザがどんどん変化している」こと。今日Webサイトを使うユーザは2年前のユーザと同じではない。具体的に挙げられたのは 「チェックアウト時(?)に押して貰う黄色いボタンをどこに配置すればユーザは戸惑わないか」 というデザイン上の問題。以前同様の画面を設計した際ユーザスタディを行ったことがあったので、その経験から最も良いと思われる位置にボタンを配置しました。しかし視線のヒートマップをとってみるとうまく機能しない。逆に以前の経験では「悪いデザイン」と思っていた配置が一番よい成績を収めた。 つまりこの2年の間に、ユーザが普段使うサイトのデザインが変化し、それにともないユーザがどういうデザインをすれば戸惑わないか、という「正解」も変化してしまった、と氏は言います。この事を "PEOPLE ARE MOVING TARGET" 人々は動いているターゲットだ というフレーズで表現する。 例えば一番目に挙げた「Gmailのチャット機能」の経験をそのまま今生かせるだろうか。氏は懐疑的です。多分今なら「チャット」といえばFacebookメッセンジャーとか(日本ならLINE)にユーザは慣れている。つまりチャット機能に期待するものが変わってしまっているのです。 それ故仮に経験がある良いデザイナーであっても失敗は避けられない。従って失敗を恐れ3ヶ月間努力してBig Surpriseを食らうよりは、Little Surprise:小さな驚き をたくさんもらったほうがいい、と主張してプレゼンテーションを締めくくりました。 このあとの質疑応答にいくつか興味深いものがあったので紹介しておきます。 「どのタイミングでレビューをするべきか?」という質問に対しては、例えば2週間単位とかで定期的に行うのがいいのではないか、と回答しています。初期の段階ではほとんどみせるものが無いわけですが、その際にはユーザのニーズを探り、簡単なスケッチを提示する。デザインが進むにつれユーザにみせるものが出来上がっていくわけです。 面白い指摘と思ったのは「プログラムを開発する際にはテストを繰り返し、デバッグすることが常識になっている。なぜデザインではそれができないのだろう」という質問でした。考えてみれば近年ユニットテストとか、Continuous Integrationというようにプログラム開発においてテストの比重は高まっています。これに対しては「デザインにおいては、まだテストがプロセスの一つとして認知されていないからだろう」との回答。 こうした「デザインの途中でフィードバックを受ける」ことの重要性は誰もが頭ではわかっている。しかしなぜそれをしないかといえば「ボロカスにけなされるのではないか」という恐れからではないか。こうした指摘に対する答えは 「レビューの仕方はずいぶん様々で、極端な例だと主観だけで相手を貶しまくり、批判されたデザイナーは帰って枕を涙で濡らすようなものもある。その逆の極端な例は皆が妙に優しく思った事をちゃんと言わないものだ。ところがその中庸で非常にうまく”批評”をしている例もある。どうやってそうした方法を身につけたのか、と聞いた所”学校で習った”とのことだった。従って良いレビューのやり方は学習によって身に付けることができる」 いやこっちが聞きたいのはその「良いレビューの仕方」なのだが、、と思うわけですが、少なくとも正しいレビューの方法は「天性のものではなく、学べるもの」というのは期待が持てるメッセージでは有ります。 いや、それでもレビューを受けて凹むのは怖い、どうすれば「最初の恐怖」を乗り越えてレビューを受けられるようになるか、という質問に対しては 「たくさん数をこなすこと。そうすればレビューで批評されても自分はそこから何かを学んでよりよいデザインができる、と確信をもてるようになる」 と返答がありました。デザイナーであってもなくても批評されるのは心理的に堪えるものです。ボロカスに言われると「ああ、この世の終わりだ。どこか人里離れた洞窟にでもこもろうか」と思うわけですが、そうした経験を何度かつみかさね、それでも自分の首がつながっている、という自信を持てれば、レビューを受けることに積極的になれるかもしれません。 質問にもありましたが、確かにプログラムを書くとき「自動テスト」を恐れる人はあまりいない。(恐れたほうが良い人がいるのも確かですが)しかしきっと遠い昔には「俺が書いたプログラムにはバグがないから、テストは不要だ」と真面目な顔で主張していた人もいたのでしょう。 同じようにデザインもテストを習慣づけるべきではないか。ではテストを行うとして、誰に聞けばいいのか。間違った相手に間違ったタイミングでテストすると恐ろしい事が起こる、、とか悩みは次の段階に行くわけです。 このビデオを見ていて、ある人が書かれていた体験談を思い出しました。 最後の学年のアート&ビジネスというクラスでした。 3ヶ月くらいかけて完成させる課題で、◯△□の基本的なシェイプを使って最終的には何かプロダクトのモックアップを作るみたいな感じだったと思います。 (中略) そして、締め切りの日の授業。みんなは自信作を早く見せてくてニコニコしてた中、先生は言いました。 「はい。みんな課題持って来ましたか?では、机の上に出して、紙の人はそのまま破り捨てなさい。立体物の人は壊してゴミ箱へ捨てなさい。」 生徒全員しばらく唖然とした状態で沈黙。その後、泣き出す人、すごい剣幕で怒り出す人、教室から出ていっちゃう人、多くの生徒はそのショックをそれぞれに表現していました。 (中略) そして、怒っている生徒に向けて先生はこう言いました。 「(中略)みんなプロのデザイナーとしてこの先の人生食っていこうと思っているなら、こんなことは日々起こること。これでショックを受けてやる気をなくしているなら、クリエイティブな職種に向かないから違う道に進んだほうがいい。クライアントの中にはアイデアや作品を見ることもなく破り捨てる人もいる。わたしもそんなこと毎日のように経験してるぞ。」 引用元: 「傷つかない技術」を体験した授業(現在リンク切れ) 少なくとも見てもらえるのはマシなほう。であれば「これは失敗ではなく、Practice」と思い切る訓練をするべきなのかもしれません。以下に引用する言葉は、デザインに限らず広く必要な心構えと思います。 多くのまだ経験の浅いクリエイターの人は、作品や仕事にダメだしされてるのに、人格を否定されたような気分に陥って、自分を否定されたように思って、まだ人に認められるような作品を作る前に辞めてしまったり、上司やクライアントと喧嘩したり、病んでしまったりってあると思います。 引用元:同上 などとエンジニアの端くれである私が、デザインプロセスについて長々書いているのは、そろそろ作ってきたものを社内でレビューしてもらわなくちゃいけないのでその心理的プレッシャーから逃れるため、ではもちろんありません。
こんにちは、上津原です。 2014/2/25 19:00より、NoSQL CouchbaseのMeetUpを、弊社ネクストで開催することになりました! http://couchbasejpcommunity.doorkeeper.jp/events/8914 アジェンダ Opening: Viber案件の事例紹介!by Eric Gold from Couchbase ( http://www.couchbase.com/presentations/couchbase-tlv-2014-couchbase-at-viber ) 1) Couchbase Server 2.5の解説 ( http://docs.couchbase.com/couchbase-manual-2.5/cb-release-notes/ ) 2) 事例紹介1 - Wizcorp( http://www.wizcorp.jp/ja/ ) 3) 事例紹介2 - サイバーエージェント( http://www.cyberagent.co.jp/ ) 4) Couchbase Serverの新クエリN1QLについて ( http://www.couchbase.com/communities/jp/n1ql ) ※N1QLとは?:SQLライクなCouchbase社が独自に開発したクエリ言語。 Couchbase ServerはNoSQLデータベースですが、N1QLを使用することによりSQLライクな検索が可能となります。 お気軽にお越しください 今回のMeetUpは、CouchbaseServerの事例紹介がメインとなります。 ちょっとNoSQLが気になるという人や、これからCouchbaseを触ってみたい、検討しているという人などもぜひぜひお越しください! 今回は、Couchbase社のEric Gold氏もいらっしゃいます。 参加申し込みは以下からお願い致します。 http://couchbasejpcommunity.doorkeeper.jp/events/8914
というわけで「難しいサンプルはわかんないから、肝心な点だけ書きます」がモットーのSimple Example第3弾は「ビューを回転させる」です。なんのことだ、と問われれば「下の動画を観てください」と答えましょう。 Rotateというボタンを押すたびに、上半分の画像がくるくる回りながら入れ替わるわけです。こういうことやりたいことありますよね?ね?ね?(ウザい) このように3Dの効果をつけながら、画面を丸ごとTransitionさせる、というサンプルもあるのですが、ここでは「画面の一部のビューだけ入れ替える」方法について説明します。ソースは github に置きました。 起動すると表示される画面を作っているのはViewControllerクラスです。でもって上半分のくるくる回るところはTransitionViewというクラスにまとめてあります。"Rotate"というボタンを押すたび、TransitionViewに対して [_transitionView changeViewType:@"cube" subtype:kCATransitionFromLeft duration:1.0]; とmethodが呼ばれるわけです。ではTransitionViewの中では何をしているか。 呼ばれるのはこのコードです。実際にViewを入れ替えているのは [ self exchangeSubviewAtIndex:secondIndex withSubviewAtIndex:firstIndex]; なのですが、それに対してLayerにanimationをつけています。やっていることはこれだけ。 ここでAnimationには様々なパラメータを指定することができます。CATransition *animationに渡しているtype,subtypeを変えることにより様々な視覚効果を得ることができる。設定可能なパラメータ(Stackoverflowからのコピーですが)はViewController.mの-(void) rotate:(id)senderに列挙しておきましたので、あれこれ変更して効果を確認してみてください。 もちろん「プログラムを使う側」からすれば、ここらへん全部別々のボタンにしておいたほうがいいのですが、そうすると読む時にボタンのコードだらけになり「あれ、どこに肝心な部分があるの?」とわからなくなる。ですからあえてボタンを設定していないのでした。手抜きではないのです(半分真顔)
ネクストでレコメンドエンジン開発をしてる古川です。 solrにおいて、複数フィールド値を組み合わせたソートを 実現する方法について紹介します。 実現方法としては、 function query を組み合わせて実現 独自のfunction query を作成して実現 独自のsearch component を作成して実現 という三つの方法があり、上から下に 実装方法: 簡単 → 大変 実行速度: 遅い → 早い 応用範囲: 狭い → 広い という特徴があります。 昨年リリースした、 「HOME'S へやくる!」 では、 2の方法で、たとえ指定した条件にすべて合致しなくても、指定した条件に、 近い順に物件リストを返すということを実現しています。 今回は、まず、1. function query を組み合わせによる実現方法を 紹介したいと思います。 以下、solr4.6.1 をベースに説明しますが、他のバージョンでも 特に問題ないと思います。 準備 solr 環境を作成 wget http://archive.apache.org/dist/lucene/solr/4.6.1/solr-4.6.1.tgz tar xvzf solr-4.6.1 テスト用スキーマ作成 ./solr-4.6.1/example/solr/collection1/conf/schema.xml を以下の内容に書き換えます。 <? xml version = "1.0" encoding = "UTF-8" ?> <schema name = "nextblog" version = "1.5" > <fields> <field name = "id" type = "string" indexed = "true" stored = "true" required = "true" multiValued = "false" /> <field name = "x" type = "float" indexed = "true" stored = "true" required = "false" multiValued = "false" /> <field name = "y" type = "float" indexed = "true" stored = "true" required = "false" multiValued = "false" /> <field name = "_version_" type = "long" indexed = "true" stored = "true" /> <field name = "text" type = "text_general" indexed = "true" stored = "false" multiValued = "true" /> </fields> <copyField source = "id" dest = "text" /> <uniqueKey> id </uniqueKey> <solrQueryParser defaultOperator = "AND" /> <types> <fieldType name = "string" class = "solr.StrField" sortMissingLast = "true" /> <fieldType name = "float" class = "solr.TrieFloatField" precisionStep = "0" positionIncrementGap = "0" /> <fieldType name = "long" class = "solr.TrieLongField" precisionStep = "0" positionIncrementGap = "0" /> <fieldType name = "text_general" class = "solr.TextField" positionIncrementGap = "100" > <analyzer type = "index" > <tokenizer class = "solr.StandardTokenizerFactory" /> <filter class = "solr.StopFilterFactory" ignoreCase = "true" words = "stopwords.txt" /> <filter class = "solr.LowerCaseFilterFactory" /> </analyzer> <analyzer type = "query" > <tokenizer class = "solr.StandardTokenizerFactory" /> <filter class = "solr.StopFilterFactory" ignoreCase = "true" words = "stopwords.txt" /> <filter class = "solr.SynonymFilterFactory" synonyms = "synonyms.txt" ignoreCase = "true" expand = "true" /> <filter class = "solr.LowerCaseFilterFactory" /> </analyzer> </fieldType> </types> </schema> solr 起動 cd ./solr-4.6.1/example java -jar start.jar & ブラウザを起動して、 http://localhost:8983/solr/ で管理画面が開くことを確認します。 データインポート sample01.csv という名前で以下の内容のファイルを作成します。 id,x,y homes1,1,4 moneymo1,2,5 lococom1,3,6 curl コマンドでcsvファイルをsolrにインポートします。 curl 'http://localhost:8983/solr/update/csv' --data-binary @sample01.csv -H 'Content-type:text/plain; charset=utf-8' curl 'http://localhost:8983/solr/update?commit=true' function query 実行 さて、ここからが本番です。solr では、 こちら にあるように様々なfunction query がデフォルトで実装されています。 例えば、x * y の値をフィールド値を取得したい場合には、 http://localhost:8983/solr/select/?q=*:*&fl=id,x,y,product(x,y) のように実行すると、スキーマに存在しないx * yの値を取得することができます。 <result name = "response" numFound = "3" start = "0" > <doc> <str name = "id" > homes1 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "product(x,y)" > 4.0 </float> </doc> <doc> <str name = "id" > moneymo1 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "product(x,y)" > 10.0 </float> </doc> <doc> <str name = "id" > lococom1 </str> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <float name = "product(x,y)" > 18.0 </float> </doc> </result> さらに、function queryを、ソート引数に指定することで、 関数値に従ったソート順でドキュメントを取得できます。 http://localhost:8983/solr/select/?q=*:*&fl=id,x,y,product(x,y)&sort=product(x,y) decs <result name = "response" numFound = "3" start = "0" > <doc> <str name = "id" > lococom1 </str> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <float name = "product(x,y)" > 18.0 </float> </doc> <doc> <str name = "id" > moneymo1 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "product(x,y)" > 10.0 </float> </doc> <doc> <str name = "id" > homes1 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "product(x,y)" > 4.0 </float> </doc> </result> function query 同士を組み合わせて、デフォルトには存在しない 関数を実現することができます。 http://172.20.12.206:8983/solr/select/?q=*:*&fl=id,x,y,if(sub(x,2),if(sub(x,1),5,1),10) インデントがないので分かりづらいですが、javascript で書くと、 以下のような関数を実行していることになります。 function (x) { if (x == 2) return 10; else if (x==1) return 5; else return 1; } 検索結果のフィールド値をみると、確かに期待した結果を得られています。 <result name = "response" numFound = "3" start = "0" > <doc> <str name = "id" > homes1 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <long name = "if(sub(x,2),if(sub(x,1),5,1),10)" > 1 </long> </doc> <doc> <str name = "id" > moneymo1 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <long name = "if(sub(x,2),if(sub(x,1),5,1),10)" > 10 </long> </doc> <doc> <str name = "id" > lococom1 </str> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <long name = "if(sub(x,2),if(sub(x,1),5,1),10)" > 5 </long> </doc> </result> 注意 function query では、フィールド値が存在しない場合、そのフィールド値は、 0が入っているものとして計算されてしまいます。 欠損値のあるデータを追加して試してみます。 sample02.csv id,x,y homes2,1,4 moneymo2,2,5 lococom2,,6 curl 'http://localhost:8983/solr/update/csv' --data-binary @sample02.csv -H 'Content-type:text/plain; charset=utf-8' curl 'http://localhost:8983/solr/update?commit=true' http://localhost:8983/solr/select/?q=*:*&fl=id,x,y,product(x,y)&sort=product(x,y) asc <result name = "response" numFound = "6" start = "0" > <doc> <str name = "id" > lococom2 </str> <float name = "y" > 6.0 </float> <float name = "product(x,y)" > 0.0 </float> </doc> <doc> <str name = "id" > homes1 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "product(x,y)" > 4.0 </float> </doc> <doc> <str name = "id" > homes2 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "product(x,y)" > 4.0 </float> </doc> <doc> <str name = "id" > moneymo1 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "product(x,y)" > 10.0 </float> </doc> <doc> <str name = "id" > moneymo2 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "product(x,y)" > 10.0 </float> </doc> <doc> <str name = "id" > lococom1 </str> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <float name = "product(x,y)" > 18.0 </float> </doc> </result> id=lococom2 の product(x,y) の値が、0.0 になってしまっています。 <doc> <str name = "id" > lococom2 </str> <float name = "y" > 6.0 </float> <float name = "product(x,y)" > 0.0 </float> </doc> フィールドが空の場合を考慮した処理を行うには、exists関数を 組み合わせてやる必要があります。 例えば、以下の例では、フィールドxの値が空の場合はデフォルト値100を 採用して、ソート順位を最下位にしています。 http://localhost:8983/solr/select/?q=*:*&fl=id,x,y,if(exists(x),product(x,y),100)&sort=if(exists(x),product(x,y),100) asc <result name = "response" numFound = "6" start = "0" > <doc> <str name = "id" > homes1 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "if(exists(x),product(x,y),100)" > 4.0 </float> </doc> <doc> <str name = "id" > homes2 </str> <float name = "x" > 1.0 </float> <float name = "y" > 4.0 </float> <float name = "if(exists(x),product(x,y),100)" > 4.0 </float> </doc> <doc> <str name = "id" > moneymo1 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "if(exists(x),product(x,y),100)" > 10.0 </float> </doc> <doc> <str name = "id" > moneymo2 </str> <float name = "x" > 2.0 </float> <float name = "y" > 5.0 </float> <float name = "if(exists(x),product(x,y),100)" > 10.0 </float> </doc> <doc> <str name = "id" > lococom1 </str> <float name = "x" > 3.0 </float> <float name = "y" > 6.0 </float> <float name = "if(exists(x),product(x,y),100)" > 18.0 </float> </doc> <doc> <str name = "id" > lococom2 </str> <float name = "y" > 6.0 </float> <long name = "if(exists(x),product(x,y),100)" > 100 </long> </doc> </result> まとめ function query を組み合わせて、新たなfunction query を作成する 方法を紹介しました。結構、柔軟に独自のスコア計算を実現できることが 伝わったのではないかと思います。 この手法では、データアクセスに非効率な部分があり、対象となる ドキュメント数が増加したり、関数が複雑になってくると、速度的な 問題が発生してしまいます。 次回は、それを回避する方法として、「 独自のfunction query を作成して実現 」 を紹介したいと思います。
こんにちは、@szkkentaroです。 最近は、がっつりAWS周りのお仕事をさせてもらっています。 皆さんはネットワークのパフォーマンス測定にどんなツールを使っていますか? 自分の場合は、以前からIperfを利用してきました。 この度、ひさびさに利用する機会がありソースをダウンロードしに行ったら、 なんと、iperf3がstableになっているではありませんか! ということで、 Iperf(ver.2系)の後継バージョンであるiperf3の使い方と追加機能についてまとめてみようと思います。 Iperfとは TCPやUDPでネットワーク帯域のパフォーマンスを測定するC言語で書かれたツールになります。 2台のサーバーにて、それぞれサーバーモード、クライアントモードとして動作させることで、 サーバ間の帯域をテストすることができます。 Iperfとiperf3の違い iperf3 is a new implementation from scratch, with the goal of a smaller, simpler code base, and a library version of the functionality that can be used in other programs. iperf3 also a number of features found in other tools such as nuttcp and netperf, but were missing from iperf2.X. iperf3は、より小さくてよりシンプルなコードをベースにして、他のプログラムから使うことができる機能的なライブラリを目指し、スクラッチから実装され直されたそうです。また、iperf2系にはなくnuttcpやnetperf のような他のツールにあるいくつかの機能が追加されています。 個人的には、ユーティリティ系の機能追加が多いような印象を受けました。新しい機能については後述します。 現時点(2014/02/07)時点での、Stableなバージョンと、ダウンロード先を表にしてみました。 名前 バージョン ホスティング 最終更新日 Iperf 2.0.5 sourceforge.net 2010/07/09 iperf3 3.0.1 code.google.com 2014/01/10 ちなみに、名前の1文字目ですが、ver2系は大文字の『 I 』で、ver3系は小文字の『 i 』を使っています。 これは、以下ソースコードのホスティング(ダウンロード)先で使われている名前と、 wikipedia に習って意図的にそうしました。 留意点 iperf3 is supported on Linux, FreeBSD (FreeBSD Ports Collection), and MacOS X only. (Windows is not currently supported.) iperf3はWindowsにまだ対応していません。 Note that iperf3.X is not backwards compatible with iperf2.X. また後方互換性もないようなので気をつけたいと思います。 インストール iperf3のインストールは、とっても簡単です。 Amazon Linuxで試した感じでは、gccを先にインストールし、 iperf3をソースからさくっとインストールできます。 sudo yum install gcc -y wget http://stats.es.net/software/iperf-3.0.1.tar.gz tar zxvf iperf-3.0.1.tar.gz cd iperf-3.0.1 ./configure make sudo make install type iperf3 # iperf3 is /usr/local/bin/iperf3 ※ Iperf(ver2系)をインストールする場合にはgccと、gcc-c++が必要になります。 基本的な使い方 サーバ側を待ち受け状態にし、クライアント側からパフォーマンステストを行います。 以下の例では、クライアント(172.20.17.181)とサーバ(172.20.17.182)間について、 3秒のパフォーマンステストを行った例になります。 サーバ側 [ec2-user@ip-172-20-17-182 ~]$ iperf3 -s クライアント側 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 Connecting to host 172.20.17.182, port 5201 [ 4] local 172.20.17.181 port 44130 connected to 172.20.17.182 port 5201 [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-1.02 sec 18.6 MBytes 154 Mbits/sec 18 [ 4] 1.02-2.02 sec 16.6 MBytes 139 Mbits/sec 4 [ 4] 2.02-3.01 sec 18.0 MBytes 153 Mbits/sec 12 - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-3.01 sec 53.2 MBytes 148 Mbits/sec 34 sender [ 4] 0.00-3.01 sec 53.0 MBytes 148 Mbits/sec receiver iperf Done. Iperfと基本的な使い方は変わっていなかったので、すぐに使うことができました。 iperf3での新機能 iperf3で追加された機能を見ていきます。 タイトルをつける -T, --title str prefix every output line with this string 大文字Tのオプションで、出力にプレフィックスをつけることができます。 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 -T `date +%F` 2014-02-03: Connecting to host 172.20.17.182, port 5201 2014-02-03: [ 4] local 172.20.17.181 port 44122 connected to 172.20.17.182 port 5201 2014-02-03: [ ID] Interval Transfer Bandwidth Retr 2014-02-03: [ 4] 0.00-1.00 sec 27.5 MBytes 231 Mbits/sec 10 2014-02-03: [ 4] 1.00-2.00 sec 25.8 MBytes 216 Mbits/sec 1 2014-02-03: [ 4] 2.00-3.00 sec 26.0 MBytes 218 Mbits/sec 4 2014-02-03: - - - - - - - - - - - - - - - - - - - - - - - - - 2014-02-03: [ ID] Interval Transfer Bandwidth Retr 2014-02-03: [ 4] 0.00-3.00 sec 79.2 MBytes 222 Mbits/sec 15 sender 2014-02-03: [ 4] 0.00-3.00 sec 78.9 MBytes 221 Mbits/sec receiver 2014-02-03: 2014-02-03: iperf Done. Iperf(ver2系)では、Tオプションは、ttlを設定するオプションなので注意が必要ですね。 -T, --ttl # time-to-live, for multicast (default 1) 詳細情報を表示する -V, --verbose more detailed output 地味に嬉しい機能です。 定期的に帯域テストをしてログ出力するような場合などは、versionやCPU Utilizationの表示が便利そうですね。 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 -V iperf version 3.0.1 (10 January 2014) Linux ip-172-20-17-181 3.4.76-65.111.amzn1.x86_64 #1 SMP Tue Jan 14 21:06:49 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux Time: Mon, 03 Feb 2014 11:36:04 GMT Connecting to host 172.20.17.182, port 5201 Cookie: ip-172-20-17-181.1391427364.967092.2 TCP MSS: 1448 (default) [ 4] local 172.20.17.181 port 44124 connected to 172.20.17.182 port 5201 Starting Test: protocol: TCP, 1 streams, 131072 byte blocks, omitting 0 seconds, 3 second test [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-1.01 sec 22.0 MBytes 182 Mbits/sec 21 [ 4] 1.01-2.00 sec 22.5 MBytes 191 Mbits/sec 51 [ 4] 2.00-3.00 sec 19.1 MBytes 160 Mbits/sec 5 - - - - - - - - - - - - - - - - - - - - - - - - - Test Complete. Summary Results: [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-3.00 sec 63.6 MBytes 178 Mbits/sec 77 sender [ 4] 0.00-3.00 sec 63.1 MBytes 176 Mbits/sec receiver CPU Utilization: local/sender 2.3% (0.9%u/1.8%s), remote/receiver 0.5% (0.1%u/0.4%s) iperf Done. はじめのn秒を省く -O, --omit N omit the first n seconds 最初の数秒を統計から省くことができます。 接続直後のネットワークが不安定な場合などに異常値を弾くことができて便利ですね。 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 -O 1 Connecting to host 172.20.17.182, port 5201 [ 4] local 172.20.17.181 port 44132 connected to 172.20.17.182 port 5201 [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-1.00 sec 18.1 MBytes 152 Mbits/sec 10 (omitted) [ 4] 0.00-1.10 sec 22.9 MBytes 174 Mbits/sec 2 [ 4] 1.10-2.00 sec 13.6 MBytes 127 Mbits/sec 0 [ 4] 2.00-3.00 sec 20.6 MBytes 173 Mbits/sec 2 - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-3.00 sec 57.1 MBytes 160 Mbits/sec 4 sender [ 4] 0.00-3.00 sec 58.4 MBytes 163 Mbits/sec receiver iperf Done. リバースモード -R, --reverse run in reverse mode (server sends, client receives) 通常はクライアントから送信するのですが、このオプションを付けるとサーバから送信するモードになります。 サーバーとクライアントを入れ替えて帯域のテストをしたい場合に簡単にできます。 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 -R Connecting to host 172.20.17.182, port 5201 Reverse mode, remote host 172.20.17.182 is sending [ 4] local 172.20.17.181 port 44154 connected to 172.20.17.182 port 5201 [ ID] Interval Transfer Bandwidth [ 4] 0.00-1.01 sec 23.5 MBytes 196 Mbits/sec [ 4] 1.01-2.01 sec 27.6 MBytes 232 Mbits/sec [ 4] 2.01-3.01 sec 24.4 MBytes 204 Mbits/sec - - - - - - - - - - - - - - - - - - - - - - - - - [ ID] Interval Transfer Bandwidth Retr [ 4] 0.00-3.01 sec 76.2 MBytes 213 Mbits/sec 28 sender [ 4] 0.00-3.01 sec 76.0 MBytes 212 Mbits/sec receiver iperf Done. JSONで出力する -J, --json output in JSON format 詳細情報をJSONフォーマットで出力することができるので、mongoDBやElasticSearchに入れたりするのに使えそうです。 [ec2-user@ip-172-20-17-181 ~]$ iperf3 -c 172.20.17.182 -t 3 -J { "start": { "version": "iperf version 3.0.1 (10 January 2014)", "system_info": "Linux ip-172-20-17-181 3.4.76-65.111.amzn1.x86_64 #1 SMP Tue Jan 14 21:06:49 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux\n", "timestamp": { "time": "Mon, 03 Feb 2014 11:37:29 GMT", "timesecs": 1391427449 }, "connecting_to": { "host": "172.20.17.182", "port": 5201 }, "cookie": "ip-172-20-17-181.1391427449.704612.4", "tcp_mss_default": 1448, "connected": { "socket": 4, "local_host": "172.20.17.181", "local_port": 44128, "remote_host": "172.20.17.182", "remote_port": 5201 }, "test_start": { "protocol": "TCP", "num_streams": 1, "blksize": 131072, "omit": 0, "duration": 3, "bytes": 0, "blocks": 0 } }, "intervals": [ { "streams": [ { "socket": 4, "start": 0, "end": 1.00007, "seconds": 1.00007, "bytes": 24641536, "bits_per_second": 1.97119E+8, "retransmits": 50, "snd_cwnd": 127424, "omitted": false } ] }, { "streams": [ { "socket": 4, "start": 1.00007, "end": 2.00013, "seconds": 1.00006, "bytes": 19398656, "bits_per_second": 1.5518E+8, "retransmits": 1, "snd_cwnd": 128872, "omitted": false } ] }, { "streams": [ { "socket": 4, "start": 2.00013, "end": 3.00011, "seconds": 0.999985, "bytes": 15990784, "bits_per_second": 1.27928E+8, "retransmits": 3, "snd_cwnd": 141904, "omitted": false } ] } ], "end": { "streams": [ { "sender": { "socket": 4, "start": 0, "end": 3.00011, "seconds": 3.00011, "bytes": 60030976, "bits_per_second": 1.60077E+8, "retransmits": 54 }, "receiver": { "socket": 4, "start": 0, "end": 3.00011, "seconds": 3.00011, "bytes": 59375616, "bits_per_second": 1.58329E+8 } } ], "cpu_utilization_percent": { "host_total": 2.26325, "host_user": 0.258657, "host_system": 2.32805, "remote_total": 0.921219, "remote_user": 0.117254, "remote_system": 0.787269 } } } 他にも CPU Affinityの設定や、zero copyでの送信など、しっかり計測したい場合のオプションが追加されています。 -A, --affinity n/n,m set CPU affinity -Z, --zerocopy use a 'zero copy' method of sending data また、linux-congestionなどはオプションが "-Z" から "-C" へと、若干変わってたりするので気をつけたいですね。 -Z, --linux-congestion set TCP congestion control algorithm (Linux only) # Iperf -C, --linux-congestion set TCP congestion control algorithm (Linux only) # iperf3 まとめ 主に、iperf3の新機能について見てきました。 基本的な操作は、ほとんどIperfと変わっていないので以前からのユーザは違和感なく使えると思います。 先に書きましたが、出力の形を整える機能や計測に対しての細かい設定ができるようになったのがありがたいですね。 また、測定結果の単位にG(ギガ)が追加されたことも時代の流れを感じてしまいました。 昔から使っている方も、新しく使いはじめる方も癖なく使えると思いますので、 是非、iperf3を触ってみてください! 参考リンク https://code.google.com/p/iperf/ http://sourceforge.net/projects/iperf/ http://en.wikipedia.org/wiki/Iperf
Apple原理主義者の大坪です。 Facebookが米国で2月3日に新しいアプリ、"Paper"を公開するというニュースが流れました。 Introducing Paper from Facebook on Vimeo . ふーん、きっと日本で公開されれば普通Facebookを見るのもこちらに切り替えるんだろうなあ。なんたって今のFacebookアプリは,,ところでこれ写真の上に文字を重ねるのどうやってるんだろう、とかのんびり考えていたわけです。 しかしのんびりしている場合ではなかった。次に出てきたのがこのニュースです。 But what you might not have heard is the story of the tool behind Paper-- Origami . Facebook developed Origami to be a rapid prototyping tool for interfaces, to allow its designers to create and test complex, original animations without the help of the coding team. via: Facebook Develops A Photoshop For Interaction Design, And It's Free For Anyone To Use | Co.Design | business + design いいかげんな訳:しかしPaperの開発に使われたツールについては聞いたことがないかもしれない。Facebookはインタフェースのラピッドプロトタイピングツールとして"Origami"を開発した。それを使えば、デザインチームは、コードを書くエンジニアの助けなしに、複雑なアニメーションを作り、テストすることができる。 引用先の表現を借りれば「インタラクションデザインにとってのPhotoshop」つまりは必携ツール、とのこと。 「iOS7からは動きのデザインが重要になるよねー。動きのデザインってどうやってやればいいのー。紙じゃ無理だよねー。デザイナーとエンジニアもっと協力しないとねー」 とか 他人ごとのように書いていた のがほぼ2ヶ月前。これがチンピラエンジニアとFacebookチームの差異というものなのでしょうか。彼らは単に嘆いているのではなく、「動きのデザイン」を可能にするツールを作成し、かつ(気前のいいことに)無料で公開しているわけです。 Origamiは単独のアプリケーションではなく、Quartz Composerのプラグインとして提供されています。Quartz Composerとはなにか?それはある人がMac OS 10.4 Tigerの「目玉機能」と称したビジュアルなプログラミング環境です。とても興味深いものの、なかなか広く使われていなかった技術でもありました。 さっそく ダウンロードページ から、インストラクションに従ってインストールしてみると、、ををこれは紛う方なき懐かしいQuartz Composerの画面だ。その上で例えばこんな動きを試すことができます。 (これは Facebookが公開している サンプルです) こうした「動き」の検討が紙の上で可能でしょうか?そして今までこうした「動き」のプロトタイプがエンジニアの悲鳴もしくは怨嗟の言葉抜きで可能だったでしょうか? 「ほら、こうやって上にスクロールするとメニューが”にゅい”っと隠れて、でもって指を離すとちょっとバウンドして」 とか言ったところで話はさっぱり進まない。 というわけで 数年ぶりにQuartz Composerの「プログラミング」を思い出したり調べたりしているわけです。下の図がその「プログラミング」の画面。一見とっつきやすそうに見えるが、思ったとおりの動きを実現するのはやはり簡単ではない。 しかし実際にPaperのデザイナー達は、これを使ってPaperのプロトタイプを作ったのです。 And in fact, almost every animation you see in Paper was prototyped first in Origami before it was handed to Facebook's programmers to build into the real app. via: Facebook Develops A Photoshop For Interaction Design, And It's Free For Anyone To Use | Co.Design | business + design Paperで使われているほとんどのアニメーションは、Origami上でプロトタイプされ、そのあと実際のアプリ開発を行うプログラマーに渡された。 つまりOrigamiで作られたプロトタイプは、デザイナーからプログラマーに渡された「仕様書」でもあったわけです。こうした新しいプロセスは正しい方向に向けた第一歩であることは間違いない。 Facebook firmly believes that Origami can strengthen the work of young designers in high school and college today, driving them to think about fluid interaction design rather than static image design. via: Facebook Develops A Photoshop For Interaction Design, And It's Free For Anyone To Use | Co.Design | business + design かなりの意訳:Origamiが高校や大学で若いデザイナー達の役に立つことをFacebookは確信している。Origamiを使うことで、静的な画像のデザインではなく、動的なインタラクションの流れについてより深く考えるようになるだろう。 近い将来、採用面接で「Origami使ったことないの?それでアプリのデザイナー志望?」と言われる日が来るのでしょうか。
Apple原理主義者の大坪です。 iPhoneはこれまで毎年新しいモデル(新しさの度合いは様々ですが)が発表されてきました。というわけで毎年新モデルが発表された直後から「次のモデルは」という噂が出始めるわけです。 Appleが新しい製品に対して非常に厳しい秘密保持を行っていることはよく知られています。しかし末端の部品サプライヤまでその厳しさを徹底させることはどうやら不可能らしい。新しい機種の発表が近づくにつれ様々な部品がリークされ、実際の発表日までにはほとんどその外観が判明します。 さて、例年のサイクルによれば秋に発表があるはずのiPhone6ですが、かなり早い段階から「今度のiPhoneは画面が大きくなる」という噂がでていました。(加えて「2種類の画面サイズがある」という情報もありますが、それはさておき) スマートフォンとか携帯電話の記事、あるいは噂を見ると「最近は大画面化がトレンドだ」というあたかも「 バスに乗り遅れるな 」といった論調を見かけることもあります。例えば横軸に年代、縦軸に画面サイズをプロットして、適当に「トレンドを示す直線」を引き「次のモデルの画面サイズはこのあたりが適当」とかやっている会社も世の中にはあるのかもしれない。しかしApple原理主義者として、Appleはそんなサイズの決め方はしない、と信じたい。 では 仮にAppleが画面サイズを大きくするとして、どのようなアプローチがあるか。9 to 5 Macの こちらの記事及びコメント欄 で活発な議論がなされています。 私なりに論点を整理しますと 仮にiPhone6の画面が物理的に大型になった場合、ソフトウェアの面から見ると以下の2つのアプローチが考えられます。 アプローチ1:画面の画素数は同じままにする。(つまり一画素のサイズが画面サイズに合わせて大きくなる) アプローチ2:画面のサイズ拡大に応じて、画素数も多くする。 記事の著者によれば、アプローチ1を採用し、画素数そのままで画面サイズを5インチに拡大したとしてもRetina(つまり肉眼で個々のピクセルが見えないくらいの解像度ということ)と名乗るにふわさしい画面の細かさが維持できるとのこと。この場合アプリ開発者は「ちょっと画面が大きくなるだけで何もかわんないもんね」と枕を高くして眠れるわけです。 しかし その場合、「なーんとなく引き伸ばした」ということにもなりかねず、そもそも何のための画面大型化か、という問題がでてくる。というわけで例えば一平方インチあたりの画素密度は同じで画面サイズを大きくし、そのぶん画素数も大きくするアプローチ2が説得力を持ってくるわけです。 ただし この場合悲鳴を上げるのは開発者です。320X480という画素数で平和に暮らしていた時代はRetina の登場で揺さぶられ、まだ大丈夫だ、大きいイメージだけ用意すればいいじゃないかと思っていた開発者はiPhone5の登場で「ぎゃっ」となったわけです。320X568ってなんだよ。画素数が変わるだけじゃなく画面の縦横比が変わっちゃうじゃないか。とかなんとか文句を言いながらも結局対応せざるを得ない。 さてAppleはどちらのアプローチを選択するでしょうか? これに対してはいろいろな立場のコメントがあり 【スペック原理主義者】5インチサイズで4Kディスプレイが必要だ! →この意見に対しては ・AppleはSpec競争にこだわったことはない ・価格とかバッテリとかどうすんだ といった反論がでています。 【現実的な開発者】AppleはiPhone5で一度開発者を「ぎゃっ」と言わせたからもう一回それが起こっても驚かないよ。もうAutolayout対応すませたもんね。(Autolayoutという仕組みを使ってアプリを作ると、画面サイズが変わっても「ある程度」自動で対応してくれるのです) 【冷静な開発者】iOS7からは、画面遷移した後戻る操作を行うのに、画面上部の「戻る」ボタンを押さなくても画面を左端からスワイプするだけで可能になっている。これはさらなる大画面化を見越した機能追加だったのではないか。(つまり画面上部に指が届かなくても大丈夫なように) 【冷静で現実的な開発者】画面サイズ変わっても、縦横比が変わらなければそんなにひどいことにならないんじゃないかな… 【小画面原理主義者】iPhoneの良さは画面がコンパクトなところだ。大きくするならAndroidに替えるぞ。 【大画面原理主義者】iPhoneの悪い点は画面が小さすぎるところだ。大きくしないならAndroidに替えるぞ。 【そもそも論】いや、問題は画面サイズではなくバッテリ持続時間じゃね?大きくして例えば12時間バッテリが持つようになればそうする意味があると思うけど。 というわけで プログラムを開発する立場としては、画面拡大に伴って画素数の変更アナウンスがでても心臓発作を起こすこと無く冷静に受け止める必要がある、と悟るわけです。多くの人が忘れているかもしれませんが、ガラケー全盛期には、Docomoのケータイだけとってみても、それはそれはものすごい数の画面サイズが存在しており、縦横比すらとっても様々だったわけです。あれに対応する事を考えれば、iPhoneの画面サイズのバリエーションなんて可愛いもんさ(涙をそっとふく) またこんな意見もあります。 【自称クレージーなアイディア】6月のWWDCで新画面サイズ+新画素数だけアナウンスだ。そうすれば秋に新画面サイズのiPhoneが発売されたときにアプリが対応できる! クレージーと自称してはいるものの、ここらへんが正解かもなあとも思えます。とはいえひょっとすると今年は「新型iPhone」は脇役かもしれない。以前からTim Cookは「2014年には新しい製品を投入する」と宣言しており、その発表のインパクトを高めるため、iPhone6は6月に発表してしまって、、、とか想像だけが膨らみます。 いずれにせよ Appleが画面を大きくするのであれば、それにはちゃんとした理由があるはず。そもそも何故大画面化する必要があるのか?それに対して「トレンドだから」という馬鹿げた理由ではない、ちゃんとした答えを出してくることを期待したいものです。それは新しいデザインかもしれず、あるいは新しい使い方の提案かもしれない。意見にもあったように「大画面化の主目的はバッテリ持続時間の向上」というのも(技術的に本当かどうかは別として)理由としては納得できる。 それはたとえばiPhone5Cが単なる「コストダウンのためにプラスチック筐体を使用した」だけのものではなかったように(Apple原理主義者はそういう捉え方をするのです。異論は受付けません) 今はただおとなしく6月だか9月が訪れるのを待ちましょう。Appleがどんな提案をしてくるのかを楽しみにしながら、そしてどんな画素数が飛んできてもくじけないだけの気力と、わかりづらいAutolayoutにも笑顔で対応できる心のゆとりを養いながら。
大坪と申します。さて、長いサンプルプログラムはよくわからない。ポイントを含んでできる限り短くシンプルに、が信条のSimple Exampleシリーズ第2弾はiOS7から可能になったViewController間のTransitionです。 ここで 「ではいろいろな種類のTransitionについて一つにサンプルで」 とやるとSimple Exampleの主旨から外れるので 「UINavigationControllerに対してpushViewControllerする場合に独自アニメーションをつけて遷移(transition)する」 だけの例を説明します。他の例(というかもっと丁寧な説明)はネット上にいくつか存在していますが、例えばこちらのブログ [iOS 7] &#x7C21;&#x5358;&#x306B;&#x3067;&#x304D;&#x308B;&#x753B;&#x9762;&#x9077;&#x79FB;&#x306E;&#x30AB;&#x30B9;&#x30BF;&#x30DE;&#x30A4;&#x30BA; - iOS &#x958B;&#x767A;&#x30D6;&#x30ED;&#x30B0; Natsu&#39;s note を参照ください。 サンプルプログラムは github に置きました。 起動するとこんな画面がでてきます。 あまりの色彩センスの良さにめまいがするかもしれませんが、気のせいです。これぐらいでひるんでいてはかっこいいTransitionなど実現できません。 まずオレンジの"Simple Transition"というボタンを押します。 するとこんな感じで画面が遷移します。慣れ親しんだnavigationControllerにpushViewControllerすればこうなるわな、という動きです。 次に赤い"Magnify Transition"というボタンを押してみます。 すると、ずりずりと中央から黄色い画面が広がる。それと同時にナビゲーションバーに"< Back"という文字が出てきます。 その文字をを押すと黄色い画面が縮小していき、後ろに青い(最初に見えた)画面が出てくる。iOS7から可能になったView間Transitionのカスタマイズ機能を使うことこういうことができるわけです。 さて、この動きの実現方法ですが... まず"Simple Transition"のほうです。ViewController.mを見ましょう。Simple Transitionボタンを押すと以下のコードが実行されます。 Pushされる(つまり次に表示される)ViewControllerを作ってPushしている。普通のコードですが、その間に謎の「//delegateをクリアする」 という行が入っています。 なんとなく不安を覚えながらも、"Magnify Transition"を押した時に実行される部分を見てみます。 ViewController.mの-(void) magnifyButtonPushed:(id)senderですが。。 なんださっきと変わらないじゃないかと思われるかもしれません。実際違うのは一行だけで self .navigationController.delegate = _transitioningDelegate; ここだけです。(Simple Transitionではnilをセットしていた) この_transitionDelegateは-(void) viewDidLoadの中で[TransitionDelegate new]として作成しています。 ではこのTransitionDelegateは何をしているかと言えばmethodは一つしか無く これだけです。if文の中身は見なかったことにして、やっていることは大枠 「MagAnimTransitionを作って返す」 ということ。であれば、MagAnimTransitionがあれこれややこしいことをやっているに違いない。というわけでMagAnimTransition.mの- (void)animateTransition:(id )transitionContext を見てみましょう。 ..... と書いておいてなんなのですが、このメソッドの中をちゃんと説明しだすととても長くなるので、ソースを見てください。(ちょびっとコメントも書いてあります)先ほど見なかったことにしたif文は、「行きと帰り」でアニメーションがちょっと違うのでそれを区別するためのフラグをたてていたのでした。 「行き」だけコードに沿って説明すると まず「行き先」のViewのサイズを0にして 次にそれをaddSubviewする。 でもってUIView animateKeyframesWithDuration:の中で「行き先」のViewサイズをアニメーション付きで戻して 最後に[transitionContext completeTransition:finished]; を呼ぶ(これ必要) - 最低限これだけやれば、サンプルプログラムにあるような動きが実現できます。そもそもここにでてきたdelegateだのクラスは何をやっているのだ、ということをちゃんと調べたい方は、前述のブログあるいは他の文章を参照ください。 私はとりあえず動くコードがあってそこからあれこれいじっていく人なので、同じような調べ方をする人にとって、このサンプルが何かの参考になればと思っております。
こんにちは、上津原です。 Couchbase [Tokyo] 2014、 Couchbase Liteの日本発公式発表があるので行ってきました。 会場はほぼ満員でした。年に一度しか無いイベントですしね。 Couchbase Server側の話は置いといて、自分の土俵のCouchbase Lite側に絞ってレポートします。 プレゼンテーション Couchbase Liteのプレゼンテーションは、ジェシカさんという女性の方が行いました。 ざっくりとCouchbase Liteとはどういうもので、どんなふうに動くのか、ということを話していて、要約すると以下の様な感じでした。 CouchbaseLiteは世界初のモバイル向けNoSQL ローカルデータをCouchbase Sync Gatewayを経由してSyncする Couchbase Sync Gatewayは、Channelを使って、閲覧可能な情報を制限することができる Channelは、ドキュメントにつけるタグのようなもの FacebookやPersonaを使って認証できるよ Syncもとっても簡単でお手軽に出来るよ パージして、モバイルローカル上のデータだけ消すこともできるよ そしてTodoLite( https://github.com/couchbaselabs/TodoLite-iOS )のデモ TodoのiOSアプリケーションで、CouchbaseLiteでデータ管理をしている。 Facebook認証も入っていて、任意のユーザとTodoを共有できるもの。 あれ、この前まで動いてなかったのに動いてる…。 聞いてみたら、最近動くようになったとのこと。これは僕も動かしてみなければ。 ジェシカさんに僕が個人的に聞いたこと Q: CouchbaseLiteは、モバイルアプリ技術者が単独で使うために作ってる?それともサーバ側の人も一緒に使う用に作ってる? A: CouchbaseLiteのSyncは、モバイル技術者が一人で開発するものではなく、サーバーサイド技術者とコラボレートして利用することを想定してるよ。 Q: CouchbaseServerを理解せずに触って、ViewとかQueryの考え方がよくわからない事が多かったんだけど、モバイル技術者のために、ViewとかQueryのチュートリアルとか無いの? A: ドキュメントがちょっとずつ充実してきてるから見てね。 ViewやQueryの使い方はCouchbaseServerとほぼ同じだからそっちの資料を参考に学ぶといいよ。 動いているものは、さっきのTodoLiteがgithubにあるからコードリーディングして学んでね。 Q: TodoLite以外サンプルアプリ動かそうとしたら動かないんだけど… A: CouchChatとかはもう全然触られてないから、あれは動かないよ。開発者も飽きちゃったみたい。 TodoLiteしか動かないと思う。ごめんね。 最後にジェシカさんから: わからないことはコミュニティかメールでで聞いてくれれば答えるよ。 あなたの問題はみんなの問題だし、わかりにくいのは良くないからね。 バグとかみつけても教えてね 。 一緒に出席してたCouchbaseServer技術者の友達にCouchbaseLiteについてどう思うか聞いた 「ものは面白いけど、SyncGatewayがセキュアなのかどうかが気になる。  SyncGatewayとCouchbaseの間になにかミドルウェアを挟んで対処する必要があるかもなあ。  ゲームだとそこをついてチートするユーザとかがいるからそこの対処ができないとしんどい。」 まとめ Couchbase Liteは使いやすいし、導入も簡単だけど、SyncするためにCouchbaseServerを導入するかどうかがやっぱり気になるところ。(もちろん、実装すればCouchbaseを使わなくてもSyncできるよ!) beta2ということもあって、まだまだ発展途上なCouchbaseLiteだけど、認証なども加わり、ネットワークと連携する強力なモバイルデータベースとなる可能性は十分秘めていると思います。 CouchbaseServerの需要が高まるに連れて、CouchbaseLiteの需要も一緒に上がっていくのかな〜というイメージです。 そしてCouchbase [Tokyo] 2014は、Couchbaseの開発者やセールスの皆さんと話ができるチャンスです。 そして英語ができない僕でも、3次会のカラオケでCHA-LA HEAD-CHA-LAをみんなで歌えちゃうくらいのフレンドリーさがあります。 次のCouchbase [Tokyo]は来年になっちゃうけど、ミートアップもちょいちょいやったりしているので、ちょっと興味があるくらいでもうれしいので参加者が増えていくといいな〜と思います。 あんまりみんな知らない、新しい、面白い技術って、わくわくするでしょ?
今回は、ここまで作ってきたデータをCouchDBと同期してみたいと思います。 この同期機能もCouchbase Liteの大きな特徴です。 Push、Pullどちらも機能を兼ね備えておりかつ、実装が簡単なのが特徴です。 普段にありがちなAPI問い合わせのメソッドなども書く必要は一切ありません。 同期対象は、CouchDB、またはCouchbase(CouchbaseはSyncGatewayが必要)となっています。 今回は、CouchDBとのSyncの方が簡単なのでそちらを見て行きましょう。 まず同期対象となるCouchDBを用意しなくてはいけません。 まずは以下に登録してください。 Cloudantに登録 Cloudant https://cloudant.com/ 値段は、使用料が毎月5ドル以下なら無料となっており、ちょっと触ってみる程度なら課金されません。 登録完了し、サインインすると、ダッシュボードが表示されます。 上部にある、New databaseに作りたいデータベース名を入力し、Createでデータベースを作成します。 これができれば準備完了です。 Replicationしよう! レプリケーション先の準備ができれば、あとはコードを書くだけです。 gist8331915 URL部分は、各自以下のとおり変更を行ってください。 yourId:先ほど登録したID password:先ほど登録したパスワード dbName:先ほど作成したデータベース名 設定できたら、viewDidLoadでこのメソッドを呼出し、Runしてみてください。 これだけで同期はできているはずです。 Cloudantの「Your database」にある、先ほど作成したdatabaseを選択し、ドキュメント情報を見てみましょう。 CouchbaseLiteで作成したドキュメントがそっくりそのまま同期されているはずです。 補足 実は、同期するだけなら、 NSArray *repls = [ap.database replicateWithURL:[NSURL URLWithString:@"https://yourId:password@yourId.cloudant.com/dbName"] exclusively:YES];&lt;br&gt; この項目だけで出来ます。 下に続いているpullとpushは、こちらが任意のタイミングでPull(GET)やPush(Post)を行うために必要になります。 任意のタイミングで同期する これも非常に簡単です。 先ほど作成した、pull、pushに対してメッセージをひとつ送るだけです。 [ pull start] [ push start] これだけで同期を開始します。 進捗を追う 進捗を追うには、最初に設定したobserverを追います。 公式では、completeを追っていましたが、時々completeしないことがあるので、modeを追っています gist8331912 CBLReplicationMode には4つのモードが有り、 kCBLReplicationStopped, / *&lt; The replication is finished or hit a fatal error. / kCBLReplicationOffline, / *&lt; The remote host is currently unreachable. / kCBLReplicationIdle, / *&lt; Continuous replication is caught up and waiting for more changes. / kCBLReplicationActive / *&lt; The replication is actively transferring data. / が存在します。 kCBLReplicationStopped になると、レプリケーション完了、もしくはエラーのモードなので、そこを終了点として持ちます。 このコードでは、同期が完了したらDBを読み直す処理をしています。 ざっくりですが、このような感じでCouchbase LiteとCouchDBを同期することができます。
サムです。 2014/1/15ついに "Google Analytics SDK for iOS" が version 3.0.3 で 64bit 対応版のSDKが公開されました。 変更ログ Google Developers にある 変更ログ には次のように記されていました。 This release contains: ・Added support for 64-bit iOS 7.0 SDK. ・Removed libGoogleAnalytics_debug.a, it’s part of the libGoogleAnalyticsServices.a library. ・Cleaned up CuteAnimals build file for Google Analytics. ・64-bit対応しました。 ・libGoogleAnalytics_debug.a を削除し、libGoogleAnalyticsServices.a に統合した。 ・Google Analytics Services SDK 内にある Exsample Code: CuteAnimals/ をクリーンアップした。 SDKの入手について SDKのダウンロードは Google Developers からできます。 また、Google Analytics は CocoaPods にも対応しており、Podfileに次のライブラリを追加すればOKです pod &#39;GoogleAnalytics-iOS-SDK&#39;, &#39;3.0.3&#39; ビルドしているアーキテクチャの確認 Xcode BuildSettings &gt; Architectures &gt; Architectures を確認する。 Standard architectures (including 64 bit) (armv7, armv7s, arm64) なら64-bitビルド Standard arcitectures (armv7, armv7s) なら32-bitビルドです。 さいごに これまで多くのDeveloperが64-bit対応を見送っておりましたが、やっとこさGoogleも対応してきましたね、これは素直に喜べます。
上津原です。 今回は、Couchbase Liteの機能のひとつである、LiveQuery機能を見ていきたいと思います。 LiveQueryとは、指定したクエリ結果に影響するデータ変更があった場合、自動で通知を出してくれる機能です。 例えば、すべてのドキュメントを呼び出していた時ならば、何かしらデータベースに変更(ドキュメントの追加、削除、更新)が行われた場合に通知が上がってくる、というものです。 これを利用することによって、 変更があった時にすぐに表示に反映する 変更があったらすぐに同期を行う などの対応などが可能になります。 コードはとってもシンプルです。 以下のようになります。 gist8143715 以上です。 KeyPathが「row」じゃなくて「rows」なのでそこに注意が必要です。 通知が取れれば、ここでpushするなり、表示を更新するなりやればリアルタイム反映が可能になります。 これで、データが編集されればそれに対して即時対応や、UIの更新などを行うことが出来、非常に便利です。 欲しい機能が予めこうやって用意されているのはうれしいですね。