株式会社LIFULLのブログ - TECH PLAY

TECH PLAY

株式会社LIFULL

株式会社LIFULL の技術ブログ

664

秋山です。 「littleBits」という気軽に電子工作が楽しめるガジェットがあります。 littleBits(リトルビッツ) littleBits は、様々な機能をもったモジュールがあり、それらをつなげることで特定の動作を楽しめるものです。 モジュール同士をつなぐには両端についている磁石でつなぐので非常に簡単ですし、間違った接続をしようとすると磁石が反発しあうので、知識のないお子様でも色々な電子工作ができるようになっています。 今回は少しだけどんなものか紹介します。 こんな箱に入ってきました。 先日、facebookで35%offのクーポンを配布していたので、勢い余ってDelux Kitを購入しました。 すでに持っていた知人からもモジュールは多いほうがいいよ、とアドバイスもらったので、そりゃそうか。多いことはいいことだ。と迷うこと無くDeluxをカートに投下。 海外発送なのですが、注文から3日で届くスピード出荷! さっそく中を開けるとモジュールびっしり。 子供に扱いやすくするためか思ったより小さく、カラフルで見てるだけでワクワクしてきますね。 ということで、眺めていてもしかたがないので軽く動かしてみましょう。 電源は基本的に付属の9V形電池を使用します。 それと、Powerモジュールを接続することで電源供給ができるようになります。 さっそくRGB LEDモジュールを使ってLEDを光らせてみます。 おわかり頂けただろうか… LEDが点灯していることを。 このLEDは今は点灯しっぱなしです。 PowerモジュールとRGB LEDモジュールの間に、Pulseモジュールを挟んでみます。 おー、LEDが点滅し始めました。 簡単ですね。 では次はServo motorモジュールを使って、動くものを作ってみましょう。 RGB LEDモジュールをServo motorモジュールに変えるだけです。 うん、これも簡単。 こんな感じに安心・安全・簡単に電子工作を楽しむことができます。 ダンボールや3Dプリンターなどを使って車を作ったり、ピタゴラスイッチのようなものを作ったり、色々と楽しめそうです。 littleBitsは年末年始、暇をしてるあなたにピッタリなガジェットではないでしょうか。 私はこれで年を越せそうです。 =========================================== 株式会社ネクストでは、一緒に世の中をもっと便利に楽しくしたい仲間を募集中です。 主体的に楽しみながら仕事できる方からのエントリーお待ちしています! http://recruit.next-group.jp/
ネクストにもOculus Riftがやって来ました! Oculus Riftとは http://www.oculusvr.com/ OculusVR社が開発中のバーチャルリアリティヘッドマウントディスプレイです。 6軸ヘッドトラッキング機能がついていて、首を右に向ければ右を見れるし、上を見れば上を見れる。 視野角も非常に広く高い没入感を得られます。 早速なので、Unity上で開発環境を作ってみましょう。 ※Oculus Rift SDKを動かすにはUnityProが必要です。 1.Oculus Riftデベロッパー登録 まずはデベロッパー登録をします。 https://developer.oculusvr.com/ 必要事項を入力し、登録します。 登録にはお金はかからないのでご心配なく。 2.SDKのダウンロード&インポート Unity 4 Pro Integration をダウンロードします。 そのファイル展開すると以下のようになります。 次に、Oculusに対応したいUnityプロジェクトを開き 先ほどダウンロードしたディレクトリの OculusUnityIntegration/OculusUnityIntegration.unitypackage をダブルクリックします。 そうすると、unityへ必要ファイルのインポートが始まります。 3.プロジェクトをOculusに対応 インポートされた OVR の中にある、Prefabs/OVRPlayerControllerをSceneにドロップすれば完了です。 ゲーム画面が2画面に分割されます。 あとは、BuildすればOK!とっても簡単ですね〜。こんなに簡単にVRの環境ができちゃうとは、すごい。 ============================================== 株式会社ネクストでは、一緒に世の中をもっと便利に楽しくしたい仲間を募集中です。 主体的に楽しみながら仕事できる方からのエントリーお待ちしています! http://recruit.next-group.jp/
さて、前回ビルドまで完了したので、今回は早速、データの作成、保存。 そして読み込みをやってみましょう。 CouchbaseLiteは、JSONドキュメントが基本です。 なので、データ自体も連想配列形式で作成します。というわけでNSDictionary形式でデータを作ります。 ViewController.mに以下のコードを追加していきます。 ドキュメント作成 流れ的には以下のようになります。 1.連番を作る ※CouchbaseLiteにはドキュメントを表すユニークなIDを作る機能はありますが、連番を作る機能はありません。 2.新規にのドキュメントを作成する 3.ドキュメントをデータベースにPutする // 新規ドキュメントの作成 - ( void )createDocument{ // Appdelegateを呼ぶ AppDelegate *ap = (AppDelegate *)[[UIApplication sharedApplication] delegate]; // カウントの読み込み NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults]; // カウントが0だったら1にする // ※連番を作る機能がないので、ここで作成する int num = [userDefault integerForKey:NUMBER_KEY]; if (num == 0 ) { num = 1 ; } // カウント文字列の作成 NSString *numberStr = [NSString stringWithFormat: @" %d " ,num]; // ドキュメントを追加 NSDictionary *contents = @{ @"text" : numberStr, @"check" : [NSNumber numberWithBool:NO], @"created_at" : [CBLJSON JSONObjectWithDate: [NSDate date]]}; // 無記名ドキュメントの取得 CBLDocument* doc = [ap.database untitledDocument]; // ドキュメントの追加 NSError* error; if (![doc putProperties: contents error: &error]){ // 失敗したらアラートを表示 [ self showErrorAlert: @"Couldn't save the new item" ]; } else { // 成功したらカウントを上げる NSLog( @"Save Complete!" ); + +num; // ユーザデフォルトのに保存 [userDefault setInteger:num forKey:NUMBER_KEY]; [userDefault synchronize]; } } // 失敗時のアラート表示 - ( void )showErrorAlert:(NSString *)message{ UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Error" message:message delegate: nil cancelButtonTitle: @"OK" otherButtonTitles: nil , nil ]; [alert show]; } CBLDocument: ひとつのデータを指します。一枚のJSONと考えて貰えれば一番わかりやすいかと。 ドキュメントそれ自体ではあるが、自身の保存、更新、削除機能もこのオブジェクトに入っています。 ここで注意する点は、databaseを使ってカラのドキュメントを作成する点です。 普通に考えると、CBLDocumentをnewすればいいんじゃ?となりますが、CBLDocumentそれ自体がPutする機能を持っておりかつ、CBLDocumentのdatabaseプロパティはReadOnlyなので後からセットすることができません。 なので、databaseと関連付けるためにdatabaseを使ってCBLDocumentを作成するというわけです。 余談:はじめDocument作って、Databaseに渡すという構図を想像してたのでチョビっとハマりました。 ドキュメントの読み込み そして次に、読み込みをしてみます。 コードは以下のようになります。 1.データを保管するArrayを作成 2.すべてのデータを読み込むQueryを作成 3.Arrayに取得したドキュメントを追加 @implementation ViewController { NSMutableArray *dataArray; //CBLデータを格納するArray } - ( void )viewDidLoad { [ super viewDidLoad]; // 格納配列の初期化 dataArray = [[NSMutableArray alloc] init]; //Documentを作成 [ self createDocument]; // すべてのDocumentを読み出す [ self loadAllDocuments]; } - ( void )loadAllDocuments{ AppDelegate *ap = ApplicationDelegate; // ArrayをClear [dataArray removeAllObjects]; // すべてのクエリを読み込む CBLQuery *allQuary = [ap.database queryAllDocuments]; for (CBLQueryRow* row in allQuary.rows) { // document取得 CBLDocument* doc = [ap.database documentWithID: row.key]; NSDictionary* contents = doc.properties; // data 追加 [dataArray addObject:contents]; } // 時間順に並び替えをする NSSortDescriptor *sortDispNo = [[NSSortDescriptor alloc] initWithKey: @"created_at" ascending:NO]; NSArray *sortDescArray = [NSArray arrayWithObjects:sortDispNo, nil ]; [dataArray setArray:[dataArray sortedArrayUsingDescriptors:sortDescArray]]; NSLog( @" %@ " , dataArray); } CBLQuery CouchbaseLiteを操作するQueryオブジェクト。 データベースで言うと、このオブジェクトにSQLを渡せばその結果がこのオブジェクト内に生成されます。 後に出てくるView機能などもこのCBLQueryを利用します。 ここでは、予め実装されている全ドキュメントを呼び出すクエリ「queryAllDocuments」を使って、全ドキュメントを取得するCBLQueryを作成しています。 CBLQueryRow CBLQuery内には、rowsというプロパティ内にクエリの結果が生成されておりその中のエンティティ的な立ち位置。 これらを実行すると、コンソールに今データベースに入っているドキュメントデータが表示されます。 起動時に1度しか追加していないので、1個しか出ないかと思います。 たくさん表示したい場合は何度か起動するか、for文でも作って回して貰えれば。 ここまでくれば、データはNSDictionaryが入ったArrayになっています。 Arrayに入っちゃえばもうこっちのもん!好き勝手利用できるようになります。やったね! では今日はこれくらいで、次回には削除、更新をしようと思います。
株式会社ネクストの花多山です。 前回の記事 海外研修 北欧に行ってきたよ - 株式会社ネクスト エンジニアBlog で 社内制度のひとつである「選抜海外研修」について触れましたが、 今回は現在開催中の制度である「クリエイターの日」について紹介したいと思います。 このクリエイターの日とは何かと言いますと、 クリエイター(エンジニア、デザイナー、プランナー等)が通常業務の枠を離れて、新たな技術やサービスの探求に取り組むことができる制度です。 希望者は四半期ごとに最大で7日間(年間合計で最大28日)合宿形式で研究・開発を行います。 某企業の20%ルールのネクスト版と言ったら分かりやすいでしょうか。 これまでクリエイターの日から生み出されたプロダクト(といっても一部になりますが)は、以下のサイトからご覧いただけます。 クリエイターの日 ただいま、第3四半期(10月~12月)の合宿期間中で、今回は11チーム(約30名)が鋭意活動中です。 活動中の風景をいくつか紹介してみましょう。 Node.jsチーム。ただいまリーダーW君(新卒2年目)の説教タイム中。 UI・UXにこだわったiPhoneアプリを開発中のようです。 黙々と作業をしておられます。 ただいま、Couchbase Liteの調査中とのことです。 お絵かきをしてますね。いったい何ができるんでしょうか? とまあ、各チーム自由なスタイルで最大7日間の合宿活動を頑張っております。 この後は、12月に社内で成果報告会を実施する予定で、どんな発表があるのか、今から楽しみにしています。
弊社(株式会社ネクスト)では、年に1回社内から7~8名程度の選抜メンバーで海外研修に行くイベントがあります。 選抜されるのは、営業や技術はもちろん、マネージャーから全社員が対象になります。 この海外研修ではある程度の金額を元に「 自分たちで行き先を決める 」という形です。 あまり他の会社では聞かない、面白い取り組みです。 海外研修は第3回で、私サムも参加することができました。 少しでも弊社(株式会社ネクスト)のことを知って頂ける機会になると思いますので、今回は海外研修について記事を書きます。 まずは、弊社がなぜこのような取り組みをおこなっているのか、その目的です。 目的① 世界視点の醸成 世界で何が起こっているか?をその目で見て、感じて、くること。その中で自分たちは何をすべきか? 目的② 部門を超えた交流の醸成 通常の業務以外でのコミュニケーションを濃密なものにし、研修の機会を通してより結束力の高いチームワークを構築する。 目的③ 成長機会の提供 この研修が皆さんの成長機会となり、個人・組織にとってより大きなフィードバックを生み出すことを期待します。 第1回、第2回と研修先がアメリカでした。 第3回目である私たちは、 スウェーデンとフィンランド に行ってきました。 まずはスウェーデンから、日本からおよそ13時間 基本的には、電車と徒歩がメインで、現地のコーディネーターさんにお願いして、色々な企業や場所を訪問するのが海外研修です。 訪問先も自分たちであらかじめ決め事前にアポを取るか、コーディネーターさんにお願いしていました。 弊社(株式会社ネクスト)は、 日本最大級の不動産・住宅情報サイト「HOME'S」 を運営しております。 そのため、住宅なども見に行ったりしました。 こっちの方は、ベランダや内装がおしゃれでした。 本屋でも、インテリアの雑誌がコーナー全体に揃っているくらいです。 訪問と訪問の間に色々と見回ることもできました。 また、研修期間中は休日を含んでいることから、土曜日日曜日は終日自由時間です。 フィンランドでは、女性経営者の朝会「 business in heart 」に参加してきました。 次に、当時のことをbusiness in heartもブログに載せてくださいました。 http://businessinheart.se/nyhet_allman/business-in-heart-far-en-kusin-i-tokyo/ 食事のレストラン決めから予約まで全部自分たちで 日程は全部で11日間だったのですが、途中で和食が恋しくなりました。 場所や企業だけでなく、有名なARABIAの工場も見学へ 筆者はアングリーバードのコップを買いました。 フィンランドの経済省と連携している vigo プログラム へ訪問 働き方や、目的など、さまざま事を勉強しました。 さらには、地元の大学へ ここでは産学協同に近い「 Startup Sauna 」というのもあり、そちらにも訪問しました。 このように、弊社(株式会社ネクスト)では、さまざまなイベントを行っております。 また、機会がありましたら、別のイベントも紹介できればいいと思います。 最後に こんな感じで楽しい海外研修でした。
株式会社ネクストの秋山です。 この度、弊社のブログにて公開しましたiOS7に関する記事に、NDAに抵触する内容が含まれていた為記事を非公開に致しました。 ご指摘頂きました KishikawaKatsumi 様、ありがとうございました。 今後はこのような事が無いよう、再発防止に努めてまいります。 今後とも、株式会社ネクスト エンジニアBlogを宜しくお願い致します。
上津原です。 前回は、Couchbase Liteの概要をお伝えしました。 今回は、実際に動作させていこうと思います。実際に動作させる私の環境は以下です。 Mac OS X 10.9 (Mavericks) Xcode 5.0.1 iOS7 先に断っておきますと、特にこの環境でないと動かせないわけではありません。Couchbase Liteのページでの動作環境は以下のようになっています。 Xcode 4.6 or lator iOS SDK 6.0 or lator ですので、ご自身の環境にあったものを使ってください。 ただし一部、Xcode5.xでプロジェクトを作成するか、Xcode4.6でプロジェクトを作成するかで設定が違います(後述) プロジェクト作成 新規プロジェクトを作成します。 テンプレートは「Single View Application」を選択します。 (もちろんこれも好きなものを選んで頂いても大丈夫です) Product Nameは「CBL_EXAMPLE」とし、DeviceはiPhoneを選びます。 その他の項目はお好きな様に設定してください。 Couchbase Lite Framework を追加する まずはこちら( http://www.couchbase.com/communities/couchbase-lite )から最新版のCouchbase Lite Frameworkをダウンロードします。 2013/11/18 時点ではcouchbase-lite-community-ios_1.0-betaが最新です。 ダウンロードしてきた、「CouchbaseLite.framework」をプロジェクトの「Frameworks」ディレクトリに追加します。プロジェクトにコピーを追加するかどうかはお好みでどうぞ。私は追加して動かしています。 次にBuild Settingsの「Other Linker Flags」に「-ObjC」を追加します。 そして最後に「Build Phases」の「Link Binary With Libraries」から、以下を追加します。 CFNetwork.framework Security.framework SystemConfiguration.framework libsqlite3.dylib libz.dylib ここで一旦エラーが出ないか確認するためにビルドをしてください。 ここで、Xcode5でプロジェクトを作成した人は警告が出るはずです。Xcode4.6の人は出ないはずです。 Xcode5でCouchbase Liteを動かすには? 現状のCouchbase Liteは、arm64に対応していません。 ですので、Xcode5の人は「Build Settings」の「Architectures」の項目を「Standard Architectures (including 64-bit)(armv7, armv7s,arm64)」から「Standard Architectures (armv7,armv7s)」に変更してください。 そうすればビルドの警告が消えるはずです。 Couchbase Lite を動かしてみる ビルドが通ればあとは実装してゆくのみです。 AppDelegateに以下を追加します。 #import <CouchbaseLite/CouchbaseLite.h> 次にプロパティ宣言を追加します。 @property (strong, nonatomic) CBLDatabase *database; そして、application:didFinishLaunchingWithOptions:に以下を追加します。 // CBLManager の作成 CBLManager *manager = [CBLManager sharedInstance]; // データベース作成 NSError *error; self .database = [manager createDatabaseNamed: @"my-database" error: &error]; データベース名に利用可能なのは、小文字[a〜z]、数字[0-9]、および特殊文字[$_()+ - /]となっています。 これでビルドが通る。 ここまでできれば、ビルドが通るようになっているはずです。 ビルドが完了すれば、アプリ内にデータベースが作成されます。 次回は、CRUD周りを見て行きたいと思います。
大坪と申します。さてiOS7専用アプリ「 へやくる! 」を開発して考えたことシリーズ(シリーズだったのか)第二弾(第一弾はなんだったのか)   iOS7での変更点、新機能は数多いですが今日はその中で特に「デザイン」(その定義がなんであれ)に関する点について。   iOS6までの実在の材質を模した意匠はiOS7ですっぱり消えました。それは時々「フラット」という言い方をされるわけですが、iOS7の基本的なDesign Principlesには「フラット」という言葉はありません。その代わり逆の意味を持つであろうDepth(深さ)という言葉が入っている。ではDepthとは具体的に何なのか?     それは画面が複数レイヤーから構成され奥行きを持っていることを知らせるため半透明の画面を使用することだったり、あるいはトップ画面のようにユーザがデバイスを持つ角度によって微妙に動きを見せることによってあたかもアイコンが浮かんでいるように見せること、と理解しています。   iOS6までは最初にJobsがOS Xを紹介した時に使った「人々が舐めたくなるようなボタンをデザインした」という言葉に代表されるこんな意匠を使っていたわけです。     こうした意匠は一見立体的であるかのように見え、深みを感じさせる、と言いたくなります。しかしそれは「まやかし」にすぎない。意匠は静的な画像であり、人々がそれに慣れてしまえばそれまでです。iOS6でのメモ帳は現実のNotepadを非常に精巧に模した意匠を持っていましたが、使っているうちそうした「工夫」はユーザの意識から消え、単なるコンピュータ上の「簡易エディタ」としてだけ使われます。   私の考えではiOS7でAppleは「静的な深さ」から「動的な深さ」に方向を変えたのではないかと。一番大切なのはユーザに見せたいコンテンツ。ボタンなどそれ以外の静的な意匠はシンプルなものにし、単独では確かに「フラット」に見える。しかしそれが「動的」に動くことによりユーザに深さを感じさせるようにした、と。   あるインタビューでIveはこう語っています。   One of the things that we were interested in doing is, despite people talked about this being “flat,” is that it’s very, very deep. It’s constructed and architected visually and from an informational point of view as a very deep UI, but we didn’t want to rely on shadows or how big your highlights could get.    引用元: Apple's Jonathan Ive and Craig Federighi: The Complete Interview   Iveの言葉は私にとって訳すのが難しいのですが、おそらくこんな意味かと 我々がやろうとしていること-”フラット”という言われ方をすることが多いが-はとてもとても深いものだ。視覚的に構成されており、情報の面から見るととても奥行きのあるUIを作ろうとしている.しかし影や、ハイライトに頼ろうとは思わない。   さて、それがデザインプロセスにどう影響するのか?   UIを持つシステムを作るうえでプロトタイプはとても重要だと言われますし、私もそう思います。問題はそのためにどんなツールを使うか。   例えばこんな製品が最近発売されたようです。   スマホアプリのペーパープロトタイピング用のノートを作成、販売開始いたしました。iPhone実寸のグリッド入り&各種バーのガイド線付ノートブックです。   確かにこの製品を使い紙の上でプロトタイプを作ることも可能でしょう。しかしiOS7から「動き」がUI設計の中で重要性を増しています。   異なる画面間の遷移を動的になめらかに遷移させるカスタムトランジション、画面部品に簡単に重力とかバネとか従来ゲームでよく用いられていた「物理エンジン」の動きを付加する機能、デバイスの動きを検知し、画面部品を動かすことができる機能等。そしてそれらによってDepthを感じさせようとしている。   紙の上で、こうした動きを含むデザインのプロトタイプが可能でしょうか?「動き」が付帯的な要素であれば「プロトだから」ということでそれらを省略することもできるでしょう。しかし「動き」がインタフェースデザインの中でより重要な意味を持つとしたら?   このようにiOS7がもたらした変化は従来「デザイナー」と「エンジニア」と呼ばれていた職種(個人的にはこのような固定的な職種の呼び方は問題があると思っていますが)の垣根についても再考を促すものです。   iOS7でどのような画面間遷移のアニメーションが可能になったかを知らずに画面設計ができるでしょうか?どのような動きが簡単に実装でき、どのような動きがそうでないのか知らずに仕様を決めることができるでしょうか?   極論を書きます。iOS7からは、デザイナーと呼ばれる人であっても、どのような動きが可能か、可能でないかを理解しなくては「良いデザイン」ができなくなるのではないか。そして実際に動くものの「手触り」を確かめずにどうして「良いデザイン」ができるか。   つまりiOS6以前よりもさらにデザイナーとエンジニアは緊密にインタラクションする必要がある、と主張したい。iOS7を作った側のトップ二人はこう述べています。   But the idea of how we could create this sense of depth, that was just the most phenomenal collaboration which required everything from motion graphics to sensing in the hardware to the most remarkable sort of algorithms from a software point of view.  引用元: Apple's Jonathan Ive and Craig Federighi: The Complete Interview   Depth-深みを感じさせるためには、モーショングラフィックスから、ハードウェアの姿勢のセンシングや素晴らしいアルゴリズムなどが互いに協力しあうことが必要だった。  つまりiOS7自体が、エンジニアリングとデザインと呼ばれているものの緊密な協力なしには成り立たないものだったのです。であれば、その上で動くアプリケーションが デザイナーが仕様を作る それを受けてエンジニアがコードを書く といった従来のやり方で作れるものでしょうか?   具体的にアプリのUIで説明しましょう。   最近Sunrise CalendarというiPhone用カレンダーアプリを使い始めました。iOS7の純正カレンダーアプリ(iPhone版)も先ほど述べたような「動き」をうまく使っていますが、これはさらに上を行っています。 例えばこの画面では下半分に個々の日の予定が表示され、上のほうに日付が並んでいます。     ここで日付が並んでいるエリアをスクロールすると、自動的にその部分の面積が大きくなり、個々の日の予定エリアが小さくなる。     ユーザがスクロールする、ということはその部分に着目しているということであり、それに応じて画面の「デザイン」を動的に変更しています。こうやって静止画だけ見ると「はあそうですか」と思うでしょう。しかし実際にiPhoneの上でさわってみると、その自然さ、ユーザを戸惑わせないコンテキスト切り替えの滑らかさに驚きます。このような「動き」を含んだデザインは静止画像だけで可能でしょうか?できないとすればどうすればよいのでしょう?   --------- 「へやくる!」の開発にあたっては開発の初期から「ワーキングプロトタイプ」を使うことを心がけました。iPad,iPhoneアプリのインタフェースがPC上のインタフェースと異なるのは「手触り」を評価する必要がある点。実機上で触って動かしてみないとそれが良いデザインか否かはわからない。   というわけで「手触り」「動き」を再現できるプロトをごりごり作る。捨てコードと割り切ってとにかく早く動くコードを作る。いわばダンボールと両面テープでモックアップを作っているようなものです。 もちろん製品のコードはそれとは別に作ります。開発の後半でもこのワーキングプロトタイプは新しいUIの検討をする際に「それなりに」役立ちました。捨てコードだから、せっかく作った部品がボツになっても泣かない(キッパリ)   とはいえ   ワーキングプロトタイプを作るのもそれなりに大変。というわけで「動き」を検討できるよいプロトタイプツールはないものですかねえ....  
はじめまして。池田と申します。   今回は、Xcode5のstoryboard上で、オブジェクトに対してAdd Missing Constraintsを適応した際の動作検証とその結果についてご紹介したいと思います。   AppleのAuto Layoutガイドを見ると、Add Missing Constraintsを適応することにより、「現在の配置の状態から必要な制約を推測し、自動的に追加する」と記述がありますが、ここでは、"推測"した結果どのような制約が追加されるのかを検証します。     Add Missing Constraintsの適応方法 storyboard上で、オブジェクトを選択し、Issues > Add Missing Constraintsを選択することで、そのオブジェクトに適応できます。   以下、独立したオブジェクトに適応するパターンと、周辺にオブジェクトがあるオブジェクトに適応するパターンでの検証結果をご紹介します。 また、検証結果は、Assistant EditorのPreview機能を用いて、 横画面の配置を確認しながら、紹介していきたいと思います。   独立したオブジェクトに適応した場合 左右位置が中央、上下位置が中央より少し上に配置されたボタンに対して適応します。 適応前 適応後   適応後は、superViewの左右中央に固定する制約と、上端からの位置を指定する制約が追加されています。横画面の配置を確認しても、左右中央、上端からの位置は縦画面の配置と変わらず、固定されていることが分かります。 今回の例では、左右中央としていますが、これを中央から少し左にずらすと、左端からの位置指定が制約として追加されます。 独立したオブジェクトに対してAdd Missing Constraintsを適応すると、左右位置の指定と、上下位置の指定が必要と推測され、どちらかの端に寄っていればその端からの位置指定、中央であれば中央固定の制約が追加される結果となりました。   周辺にオブジェクトがあるオブジェクトに適応した場合 次に、周辺にオブジェクトがあるオブジェクトに対する適応を検証してみたいと思います。 中央にボタンを配置し、その上下左右にボタンを配置した例で検証します。 適応前 適応後(中央のボタンに)   Add Missing Constraintsを適応したのは、中央のボタンになりますが、周辺のボタンの配置に対しても影響を及ぼしていることが分かります。 具体的な影響としては、中央のボタンから、周りのボタンへの間隔の制約と、整列の制約が追加されています。 横画面の配置を確認すると、中央にボタンが配置され、上下左右にボタンが配置されるという、縦画面の配置と同様の配置になっていることが分かります。   では次に、さらにオブジェクトを増やし、複雑なレイアウトにしてみようと思います。中央の上下左右にボタンを配置し、その更に上下左右にボタンを配置してみます。 適応前 適応後(中央のボタンに) 横向きの配置を確認すると、Add Missing Constraints適応後、中央のボタンと、その上下左右のボタンは縦画面の配置と同様の配置になっていますが、その外側のボタンは縦画面の配置とは異なった、意図しない配置になっています。 また、エラーが出ており、ここのエラーは、一番外側の4つのボタンの位置指定に対する制約が不足している旨のエラーが出ています。 そこで、制約が足りない4つのボタンに対して、それぞれAdd Missing Constraintsを適応します。 すると各ボタンに位置指定の制約が追加され、以下のようになります。 横画面の配置を確認すると、今度は、縦画面の配置と同様の配置となっており、エラーもなくなっていることが分かります。   まとめ 以上から、Add Missing Constraintsの適応は、 周りにオブジェクトがある場合、他のオブジェクトの配置にも影響を及ぼす 複雑なレイアウトの場合、完全に制約を追加しきれない場合もある などの注意点もありますが、一発、または複数回、この設定を適応していくことで、自動的にレイアウトを調整できる場合もあります。 この機能を最初見たときは、"推測"で制約が追加されるなんて、何が起こるか全然分からないし、なかなか使えないという思いが結構あったのですが、実際使ってみると、あ、うまく使えばレイアウトの設定、意外と省力化できるかもという感覚を持っています。 最終的には、オブジェクトのサイズの調整であったり、なんらかの調整は必要になってくるとは思いますが、まずざっくりとしたレイアウトを決める、画面サイズによらず、中央にあるレイアウトでオブジェクトを配置する、など、場合に応じて使ってみるのもよいかと思います。 設定しても、アンドゥですぐ戻せるので、一回試してみるのもありだと思います。   以上、今回の検証がレイアウト設定をされる際の参考になれば幸いです。
株式会社ネクストでエンジニアをやっております サム と申します。 突然ですが、iOSアプリケーションを開発したことがある人は必ず iOSシミュレータ でテストを行っていると思います。 しかし、そのテストは 動作確認だけ やっておりませんか? 実は、後少しだけ確認することで、 レンタリングの速度などを改善 することができます。 これにより、アニメーションなども比較的効率良く実行することができます。 今回は、意外に知っていそうで知らない「 iOSシミュレータのデバック 」について記事を書きます。 iOSシミュレータのメニューに「 デバッグ 」という項目があります。 こちらに次の4つの項目があると思います。 ブレンドレイヤー (Color Blended Layers) コピーイメージ (Color Copied Images) 不揃いのイメージ (Color Misaligned Images) オフスクリーンレンダリング (Color Off Screen Rendered) 今回は、よく使われる「ブレンドレイヤー」および「不揃いのイメージ」について説明します。 ・ブレンドレイヤー レイヤーが重なり合っている部分を色分けして表示してくれます。 複数のレイヤーが重なって描画されている部分は、赤の強調表示になります。 これを解消するには、対象となる部品の 背景色を透明ではなく、背景色と同一色に変更 することです。 赤い部分をなるべく削減 することで、描画性能が大幅に向上します。 ・不揃いのイメージ ソースピクセルがデスティネーションピクセルに揃っていない画像上に、黄色をオーバーレイして表示します。 次のソースコードを実行してみると - ( void )viewDidLoad { [ super viewDidLoad]; UIImage *homesKunImage = [UIImage imageNamed: @"homes_kun" ]; UIImageView *imageView = [[UIImageView alloc] initWithImage:homesKunImage]; // 画像を画面中央に表示する imageView.center = self .view.center; [ self .view addSubview:imageView]; } iOSシミュレータ上では正しく表示されているように見えます。 けれども、ここでiOSシミュレータにある「不揃いのイメージ」にチェックを入れると、次のように見えます。 このように、 見た目では正しく表示されているように見えても、実際には正しく表示されていない ことがよくあります。 この原因をデバッグログから探ってみます。 まず UIImageView の center を設定したときの前後でイメージビューの フレームの変化 を確認してみます。 NSLog( @"before: %@ " , NSStringFromCGRect(imageView.frame)); // 画像を画面中央に表示する imageView.center = self .view.center; NSLog( @"after: %@ " , NSStringFromCGRect(imageView.frame)); これを実行すると、次のようにログが出力されます。 before: {{0, 0}, {120.5, 141.5}} after: {{99.75, 213.25}, {120.5, 141.5}} 最初のフレームの座標は {0,0} の整数に対して、中央表示後は {99.75, 213.25} と浮動小数点になっています。 こちらが原因なのです。 では、この浮動小数点を丸めれば解決しそうですので CGRectIntegral を使った、次のソースコードを実行してみます。 NSLog( @"before: %@ " , NSStringFromCGRect(imageView.frame)); // 画像を画面中央に表示する imageView.center = self .view.center; // フレームの値をまとめる imageView.frame = CGRectIntegral(imageView.frame); NSLog( @"after: %@ " , NSStringFromCGRect(imageView.frame)); このような問題は、 画像サイズが奇数で作成されていることで発生 します。 こちらのデバッグログは before: {{0, 0}, {120.5, 141.5}} after: {{99, 213}, {122, 142}} 画像フレームサイズも丸められております。 そのため、 実際の画像サイズと表示されてる画像フレームが異なった ため、黄色にオーバーレイされているのです。 そこで、フレームサイズを元の画像に合わせてあげます。 NSLog( @"before: %@ " , NSStringFromCGRect(imageView.frame)); // 画像を画面中央に表示する imageView.center = self .view.center; // フレームの値をまとめる imageView.frame = CGRectIntegral(imageView.frame); // フレームサイズを調整する imageView.frame = CGRectMake(CGRectGetMinX(imageView.frame), CGRectGetMinY(imageView.frame), image.size.width, image.size.height); NSLog( @"after: %@ " , NSStringFromCGRect(imageView.frame)); いままであったオーバーレイはすべてなくなりました。 こちらのデバッグログは before: {{0, 0}, {120.5, 141.5}} after: {{99, 213}, {120.5, 141.5}} と、フレームの座標のみを設定したものになります。 このように、ほんの少し気を使うことで、アプリケーションはより快適に動作することでしょう。
大坪と申します。先日 TechCrunch Tokyo 2013 に参加しました。そこで聞いた内容のうち興味深かった点、そこから考えたことなどについてつらつらと。 一番おもしろかったのはWebサイト構築サービスWeeblyの創業者でCEOのDavid Rusenko氏の講演でした。 こちら に講演者の顔写真があります。 この写真の見たままの「とても若い」Rusenko氏。ばりっとスーツを着て立派なビジネスマン、、というよりはがんばってスーツを着ている高校生のようにも見えます。しかし話の内容はとても「高校生」などと言えるものではありませんでした。 学校(Penn State)を辞め、起業してサイトを作ったのはいいが、登録してくれる人はせいぜい二桁の前半。メディアにとりあげられたりすると一時的にユーザが増えるのだけど、そのあと元に戻ってしまう。ずっとそんな状態が続いた。ここらへんの話は自分でサイトを立ち上げたり、アプリをリリースしたことがある人ならば身につまされるはず。 一時期は銀行残高が$100をきった。そこで$650Kの資金調達に成功したけど、それを得るための書類の厚さはものすごかった。 スタートから18ヶ月たったが、ユーザは本格的には増えなかった。しかし20ヶ月で状況が好転しだした。ほっとしたのもつかのま、34ヶ月で貯金がつきかけた。何を払わなくちゃいけないか、何をのばせるかを必死に検討した。 48か月たってでコンスタントにユーザが増え始めた。今はユーザも増え、満足度も高い。(ここから現在成功している様子が語られますが、彼のスピーチの主眼はそこになかったように思います) そうした経験を語った後に、彼は3つ学んだことを挙げました。 #1 you can't succeed if you quit. 辞めたら成功できないよ 彼らは一年半も鳴かず飛ばずの状態が続き、ユーザが増え始めたのは4年後。それだけにこの言葉には重みがあります。もちろんいつまでも成功しないやり方を続ける人もいるわけですが、確かに辞めたらそこで終わり。 この講演では明確に述べませんでしたが、その間彼らはずっと次の項目にフォーカスしたのだと思います。 #2 Make something people want. 人々が欲しがるものを作れ ユーザに受け入れられることが一番重要。特許だの他のことはそれに比べれば細かなこと。 この言葉を「当然だ」と思う人はとてもラッキーな人か、働いた経験が浅い人だと思います。「人々が欲しがる物」ではなく「すぐ収益がでるもの」「自分たちが作りたいもの」「お客様に言われたもの」「上司が言ったもの」,,etcを作ることになります。 #3 Don' always listen to advice. アドバイスに従わなくてもいい この後講演したDisqus 創業者兼CEOのDaniel Ha氏は同じ内容をより詳しく述べていて advice = limited life experience + overgeneralization アドバイス=限られた経験+過度の一般化 いろいろな会社で働くと実感することですが「会社」とか「仕事」とか「常識」という言葉が意味するものは本当に様々です。ある会社では賞賛される行為が、別の会社では批難の対象となる。そしてどちらの会社もちゃんと法律を守りそれぞれ成功している。そうした状況を何度も経験します。 そうした経験を通じ、結局人間一人が経験できることというのはそれほど多くはないのではないか?と考えることになります。もしそうなら「他人に対するアドバイス」とは、その人の限られた経験だけを元に全く異なる文化の中で生きているかもしれない人の行動を評価することにほかなりません。 もちろん「普遍の真理」というのは存在し、それには耳を傾ける必要があります。しかし全てのアドバイスはそこまでの普遍性を持つものか? - Techcrunchから少し離れますが、Googleの元CEO Erick SchmitはGoogleに来た時このように振る舞ったと言われています。 エリック(・シュミット)は新しいゲームをプレーするために、 自分の振る舞いを完全に変えた。エリックほどの成功者であれば、 自分は万事心得ていると考えても許されるだろう。グーグルにやって来て、 おい青二才、俺がやり方を教えてやるよ、と彼が言ったって許されたはずだ。 そうではなく、彼は話を聞いた。そして観察した。 何が起きているのかを見極めた。 自分がこれまでうまくやってきたことのうち、 どの部分がグーグルをより良くするか、見極めたのだ。 (引用元: 文藝春秋|梅田望夫著「ウェブ時代 5つの定理」|ビジョナリーたちの名言リンク集 第5定理「大人の流儀」  ) おそらくErick Shumitはadviceについての先ほどの式を(明示的かどうかは別として)理解していたのだと想像します。自分の経験をそのままGoogleに持ち込もうとするのではなく、自分が経験した範囲でどのようなことがGoogleに活かせるのか慎重に考えたのだろう、と。 ここがGoogleのCEOと、酒の席でくだまいている「説教オヤジ」(あるいはそれに類する人達)の差異なのだろうと考えるわけです。 - もう一点印象深かったのはAirbnbのOle Ruch氏が言ったこの言葉。 100 people who love you is better than 1 million who kind of like you. 100万人に好かれるより、100人に愛されるべき 熱心なファンが100人いれば、その人達はサービスの良さを自ら伝えてくれる、と。この言葉をなんとなくは理解していても、その実現のために具体的な行動を起こしている会社はあまり多くないと思います。サービスが愛されるとはどういうことなのか。そのために何をして、どのようにその結果を計測するのか。それよりは「アクティブユーザ数」「セッション数」「クリック数」などのlike か loveかわからない数字だけを眺めている企業のほうが多いのではないでしょうか。 などと書いているとだんだん耳が痛くなっているのでこのへんで。 - 追伸:同じくTechCrunch Tokyo 2013に参加された方が書いた ひろゆき’s 意見: 「Strikingly」CEOが「TechCrunch Tokyo」で伝えたかった3つのこと にも共感を覚えます。StrikinglyのCEOが強調したことは以下の3点でした。 Don't quit Build half a product Create super fans - 追伸:本家の記事 「大事なのはメディアに載ることよりも諦めないで続けること」Weebly創業者が語る成功の3つの秘訣 | TechCrunch Japan が出ました。
大坪と申します。話題になっていた こちらの記事 に便乗して、私たちがどうして「 へやくる! 」をiOS7 onlyにしたのか。その結果はどうだったのかについて書きます。   へやくる!のコーディングが本格的にスタートしたのは、私がWWDC2013から帰って来た後の事でした。初めてお披露目されたiOS7には圧倒されました。ちまたでは   「とうとうiOSもフラットデザインか」     という言われ方をしていましたが、WWDCに参加していた人は誰一人そんな印象を 持たなかったと思います。それはまさにiOSのインタフェースはどうあるべきかを考え抜いた、深い洞察の具現化だと感じました。(スキューモーフィズムを好んでいたと言われるフォーストールが去ってから短期間でこれほどまでの変化を成し遂げた開発力には感服しますが、それはまた別の機会に)   iOS7がいつ正式リリースになるかの発表はもちろんありませんでしたが、毎年のサイクルからすると、どうも「へやくる!」のリリース時期と重なるような気がする。さてどうしよう?もちろん最新OS対応にしたいという気持ちはありましたが、すぐに全てのiPadユーザがiOS7に移行するわけではなかろう。まあ   「iOS6ベースで。ただしデザインはiOS7テイストを取り入れる」   ところかな、と思っておりました。   定例会議でそうした構想を話したところ、iOSサイドの開発を担当する人間から   「いや、ここはiOS7 onlyで最新のインタフェースを取り入れましょう!」   という言葉がありました。最新OS対応と言えば聞こえはいいですが、開発する側からすれば、サンプルもない、ドキュメントも限られる、NDA期間中はおおっぴらに情報交換もできない、といった茨の道を歩む事を意味します。 それを承知してなおiOS7 onlyを目指すとは。「へやくる!」自体挑戦的なUX実現を目標にしていましたので   「なんという心強い言葉。君がそういうなら、是非その方向で行こう!」   と言いました。先ほど述べた最新OS故の苦悩は全部iOSソフト開発者の肩にのしかかるわけですが、それを担う人間が挑戦しようというのだから「部外者」が止める理由は無い、などとのんきに考えておりました。   まさかそのクライアント開発が 自分に降ってくる とは夢にも思わず。     仔細は省きますが、クライアントの検索条件設定と物件一覧のところは自分で書くはめになったわけです。iOS7専用にしたんだから、新しい機能を取り入れよう、といくつかUI部品を試作しました。WWDCではプレゼンをする人が口々に「もうサンプルはサーバーにアップロードしてあるよ」と語っていた筈なのに、待てど暮らせどサンプルコードは利用可能にならない。結局WWDCのセッションビデオを何度も見直すはめになります。しかしそれをなんとか克服し、物理エンジンを取り入れたスライダーとか、UINavigationControllerのTransitionとか、あるいはメモ帳みたいなちょっとへこんで見える文字とか作りました。   しかし結論から言えば、これらは最終的な製品には反映されませんでした。理由は単純で、従来の部品と比較した時にUX上でのメリットが見いだせなかったからです。「へやくる!」はユーザに新しいUXを提供することを目標としており、iOS7の新機能のテスト台ではない。ですから土曜日をつぶして作った部品が月曜日にあっさり没になる、ということが何度も起こった訳ですが、そのこと自体誇るべきことだと思っています。(一エンジニアの立場では”しくしくしく”ですが)   リリースした後になってみると、iOS7対応ということで、App Storeの該当セクションに掲載していただいたり、あるいは「おすすめアプリ」として取り上げていただいたりしたことが、新規ユーザを増やすという点で大きな効果があったことは事実です。しかし一エンジニアとして考えた時にその最大のメリットは「簡素な美しさ」でした。   それは何のことか?   アプリを開発する人間はおそらく一番多くアプリ画面を眺めることになります。特にUI部分を担当するときはそうです。リリースが近づいてくるといろいろなことが起こり、疲労もたまり行き詰まったように感じることもあります。   変な言い方ですが、そういう時にiOS7とそれにマッチした簡素で美しい画面(これはiOS7のコンセプトを理解した上で画面をデザインしてくれた人の努力の成果です)と相対しているのと、かつての「ガラケーサイト」のようなデザインを観ているのとではモチベーションの湧き方が全然違う。少なくとも私にとってはそうです。自分たちは美しい、すばらしいものを作っているのだという思いは、よれよれになったエンジニアにとって「砂漠で死にかかっている男に対するコップ一杯の水」くらいの効果があるわけです。   iOS6までとiOS7ではデザインに対する考え方が大きく変わりました。もちろん両方の環境で動作するアプリを作る事はできる。しかし「デザイン」というのは表面的な意匠にとどまる物ではない。iOS7のデザインテーマとして挙げられているClarity, Deference,Depthというコンセプトについて深く考えれば、自ずと意匠だけではなくユーザとのインタラクション、画面設計自体が変わってくる事に気がつくでしょう。つまりiOS6とiOS7の共用アプリを作ろうとすればこうした事実についてかなり慎重に 考える必要がある、と主張したい。(具体例についてはまた別の機会に書くかもしれません)   WWDCでAppleが行ったiOS6/iOS7共用アプリについての説明は非常に歯切れが悪いものでした。一体何を言っているのだろう。考えたあげく、私は   「要するに作り直せ、といっているのだ」   と結論づけました。よれよれになりながらもようやくiOS7専用アプリをリリースした今では、そのことをより確信をもって語ることができます。   もちろんアプリ開発の事情は様々だと思います。あるバージョンのOSを使っている人が何%をきったらサポートから外す、というのは常に難しい判断です。しかし今回iOS7専用でアプリを開発するチャンスを得られた事は非常に幸運だったと今は考えています。
はじめまして、上津原といいます。 スマホアプリの組み込み系のデータベースとして、SQLiteしか選択肢がなかった最近ですが、NoSQLのCouchbaseが「 Couchbase Lite 」という、モバイル用のNoSQLフレームワークをリリースしました(まだベータですけど)。 スマホ開発者の一員として、触らねばなるまいということで動かしてみました。 今回は、大体のCouchbaseLiteの特徴や使い勝手についてお伝えします。 そもそもCouchbaseってなんなのよ Couchbaseは、ドキュメントベースのNoSQLで、よくMongoDBと比較の引き合いに出されます。 もともとはCouchDBというオープンソースデータベースがあり、それを商用版にしたのがCouchbase、という感じで考えてもらえればわかりやすいかと思います。 それじゃあCouchbase Liteって何者なのよ そのCouchbaseみたいにモバイルデータベースを動かそう、というのがCouchbase Liteです。 実際にCouchbase Liteというデータベースが生成されるわけではなく、裏っかわではSQLiteがデータ保存をしています。 つまり、CouchbaseのようにSQLiteを操作するインターフェースやAPIをCouchbase Liteが提供している、というのが正しい考え方になるか思います。 Couchbase Liteの特徴 じゃあそのCouchbase Lite、こいつはSQLiteとどうちがうの?という話になってくるのですが、以下の様な特徴があります。 ・JSON形式でのデータの取扱 Couchbaseが「JSON Everywhere」と提唱している通り、データ形式はJSONとなります。 実際に扱うと、それぞれのドキュメントは連想配列のオブジェクトとして扱われているため、データのパースなどの面倒くささがありません。 ・同期が非常に簡単 Couchbaseはもともとレプリケーションがとても簡単なデータベースです。 その特徴をCouchbase Liteも引き継いでおり、とっても簡単にサーバー上のCouchbaseと同期することができます。 以前は、CouchDBとしかSyncできなかったのですが、今回SyncGatewayがリリースされ、それを介することでCouchbase ServerとのSyncも可能になりました。 ・データ型・Date型なども簡単に保存が可能 Objective-Cで言う、NSDataやNSDateをそのまんま渡せば保存することが可能です。 NSDataに関しては自動でBase64に変換するように作られています。 なので、特にデータの変換を意識することなく、データを保存してくれます。 ・ユーザごとの認証機能が付いている まだ試してはいませんが、Facebookを使った認証機能なども付いているようです。 これもおいおい試してみようと思います。 大体は以下のスライドにも書いてあります。 Couchbase_for mobile_Couchbase_SF_2013 from Couchbase   サーバーサイドよくわからないけど、ウェブサービス作りたいモバイル開発者におすすめかも。 私自身がサーバー周りの開発はしない、という背景もありますが、サーバーサイドわかんない、面倒くさいという人にはおすすめだと思います。 アプリを作ろうとすると、やっぱりサーバーが必要で、認証が必要で、とサーバーサイドの悩みが増えてきて誰かやってくれる人いないかな~なんて考えているうちにやる気が失せていきます。(少なくとも私はそうです) だけど、Couchbase Liteは ローカルでDB保存 → 連想配列で簡単!! サーバー通信 → Syncすればそれでできる! ユーザー認証 → Couchbase Liteがやってくれる SQL?なにそれ美味しいの? → JSONならわかるよ! あれ?なんだかいい感じ。 と言った感じで、モバイル開発者だけでサーバーサイドの実装なしにサーバ同期型のアプリは作成可能になりそう。 といったところで、今回はここまで。 次回は、Xcodeで実際にCouchbase Liteを動かしてみましょう。  
というわけで楽天テクノロジーカンファレンスの報告第2弾にして、私のメインミッション。ライトニングトークでの「 へやくる! 」発表です。しかしそこに至るまでの道のりについても書かなくてはと思うのです。     楽天テクノロジーカンファレンスに参加するのは去年に続いて2度目。子供の授業参観が終わってからかけつけたので、到着したのはカフェテリアが閉まる30分前。いそいで13Fにあがります。   メニューは2種類用意されており、そのなかからカラアゲを選択。副菜も二品ついて、普段の「コンビニ塩おにぎり一個」とはレベルの違う昼食を御馳走になりました。   この時点で既に楽天の方々のホスピタリティを実感することができます。エレベータホールに必ず案内の人がいるし、各エレベータにも黄色いTシャツを着た社員の方が。カフェテリアにも「何か困った人はいないかな?」といった雰囲気で社員の方が立っていてくれます。使用済みの食器を下げるところが少し離れているのですが、ちょっと聞けば丁寧に教えてくれる。 東京オリンピック実行委員会は、是非楽天テクノロジーカンファレンスに参加して「おもてなし」とは何かを実感すべきだと思う。(真顔)   さて、   先日書いたMatz氏の基調講演 の次に聞いたのが"Innovate or Die"という題名で、楽天社内でどのようにイノベーションに取り組んでいるかの紹介。質疑応答が非常に盛り上がりました。過去の重要特許を見てみると、大企業からでているようだが?いや、そもそも大企業からイノベーションがでていないとは言っていない、とか様々なやりとりが続きます。質問しているのがEnglish Speakerばかりというのが少し残念でした。これは言語としての英語に対する壁がある、というだけの問題ではないように思います。   自分の出番前に聞いたのが"The approch of Big Sale in Japan Ichiba".平たく言えば   「突発的なトラフィック増とエンジニア達との果て無き戦い」   楽天スーパーセール、あるいはプロ野球での楽天優勝の際に、 楽天のインフラがどのような試練にさらされ、 どういう問題が生じたかを赤裸々に語ってくれます。なんでも最初のスーパーセールの際の反省点を活かすため、2度めは実運用環境で負荷試験を行ったとのこと。すごいなあ。自分たちの失敗事例とその対策をここまでオープンに語ってくれる ことに感動しました。   といったところでライトニングトークの時間が迫ってきます。発表者は前の方の一角に順番に座る。深呼吸をしていると   「英語でプレゼンする機会はありますか?」   と声をかけてもらえました。そうだなあ。英語でプレゼンしたのは去年のこのカンファレンスか.. 自分で発表することのディメリットは、緊張のあまり自分より前の発表が耳に入らず、終わった後の開放感のあまり自分より後の発表が耳に入らないことです。従って他の方の発表についてここで書くことはできません。自分がしゃべったことの要旨を書いておきます。私のカタカナ英語では理解しがたいところも多々あったと思うので。使った動画も合わせて載せておきます。    ・ネクストの大坪といいます。今日は我々は開発したiPadアプリ「へやくる!」について話します。このアプリは不動産情報を検索するために設計されています。   ・不動産情報検索アプリは、他にも何種類か存在しています。しかしほとんどのアプリでは情報とユーザの距離が長過ぎる。具体的にお見せしましょう。   ・例えばあるアプリでは、起動してから最初の不動産情報を見るまでに10回も画面をタップする必要があります。いくらなんでも長すぎます。情報にたどり着くころには、ユーザは何を探していたか忘れてしまうかもしれません。   ・「へやくる!」はこの点どうか?起動すると、使っている場所近くの不動産情報を表示します。ここからユーザは好きな様に検索結果を変更できる。例えば東京に住もうと思ったとして、(東京の住宅事情について詳しくないので)有名な東京の駅を設定してみましょう。東京、新宿、池袋、それに渋谷。(ここで結果がアップデートされる)   ・なかなかいい結果です。しかし月に5万円以上は払えない。この条件を設定するためには、ドラッグ-ドロップして指をくるりと回す。たった2タップです。駅から10分以上歩きたくない?ドラッグ-ドロップしてタップ。同じく2タップ。このように「へやくる!」ではほとんどの場合1-2タップで条件を設定したり変更することができます。     ・このように情報とユーザ間の距離が近いので、「極端なケース」を試してみたくなるかもしれません。例えば「お金持ちの生活を覗いてみたい」、と月の家賃を200万円に設定してみましょう。するとでてくるのは豪華ホテルのような写真です。     ・ここで「間取り表示モード」に切り替えるとあることに気が付きます。高額物件の間取り図はほとんど英語で表記されている。理由はよくわかりませんが、もし豪華な家に住もうと思えば、まず英語を勉強する必要があるかもしれません。(注:これは最後までいれるかどうか躊躇したボケでした。笑ってもらえてほっとしました。実はこのあと「楽天社員が全員英語をしゃべる理由がわかったような気がします」というボケをいれようかと思ったのですが、さすがにそれは思いとどまりました)     ・逆のケースではどうでしょう?東京に住みたいけど、月2万円以上は払えない、という場合でもちゃんと物件が見つかります。検索結果の写真を見ると、学生時代を思い出します。これは日本の昭和スタイルのアパートです。     ・というわけで、もし引っ越ししようとかアパートを探そうと思っているなら是非このアプリをダウンロードして使ってみてください。引っ越しの計画など全くない、という方も是非使ってみてください。今日お見せしたように条件設定・変更がとても簡単にできるので、考えうる限り「一番変な」条件を試してはどうでしょう。結果にきっと驚くと思います。   --- といったところで無事しゃべり終え、プレゼンを終えます。今回の制限時間は4分間で、時間がくるとドラがドシャーンとなる。私がプレゼンを終えたのは、ちょうどドラを鳴らそうとしたところだったようです。   英語でプレゼンするのは心理的にとてつもないプレッシャーですが、English Speakerの観客に聞いてもらえるというメリットがある。English Speakerの人たちはリアクションがはっきりしています。ドラッグ-ドロップしたところとか、東京中心で200万円の物件、2万円の物件を出したところで   「ををー」   という声が聞けたのは嬉しい限りでした。   ライトニングトークが終わると、楽天アワードの発表。その後は皆で13Fに移動しビアバッシュ。こちらはプレゼンが無事終わったのでご機嫌モード。何人かの方からプレゼンの感想を聞かせてもらったり、いろいろ興味深い議論ができました。   私は朝と夜が早い人なので、途中でおいとまします。出口付近に箱がいくつか置かれており、"Excellent",(真ん中忘れた)、"So So"と書かれています。今日使った名札の台紙を 箱にいれることで、今日の感想を伝えられる仕組みとのこと。台紙をExcellentに入れるのに躊躇はありませんでした。   楽天は常に新しいことに取り組んでおり、それにはいろいろな意見があると思います。しかしこのカンファレンスで感じた「楽天社員のおもてなしの心」「エンジニア達の技術にかける情熱」は疑いようがない。もっと多くの人がこのカンファレンスに参加したり、発表したりしていろいろなことを考える切っ掛けになれば、と思いました。
Apple原理主義者であることを公言している大坪と申します。 Apple原理主義者なので、Appleに関する記事だったら、 英語であってもがんばって読もうとします。「おもしろい」 と思うと訳する力もないのに紹介したくなります。 というわけで 「面白いと思ったところだけ訳します。 訳せないところは抜かします。 気になる方はどうぞ原文にあたってください」   がモットーのAppleに関する英語記事紹介シリーズ。 第一弾は「謎のEjectボタン」(原文: http:// www.dadhacker.com/blog/?p=2080 )   先日Appleの新キャンパスが話題になりました。 まるで宇宙船のような円形の建物がCupertinoの 市議会に 承認された とのこと。 では今あるキャンパスはどうなるのでしょうか?そこには「 謎のEjectボタン」があるらしいのです。   ちなみにejectを 辞書 で調べてみると 《 野球 》 退場させる ,  追い出す  とか ( 飛行機 などから) 緊急脱出 する. とかいう意味がでてきます。 話は1992年。今のAppleのビルが新築だったころのこと。ビルができて 一番目か二番目に引っ越してきたのがNewtonのグループでし た。なぜかというとNewtonはジョン・スカリー( 当時AppleのCEOだった人です) のお気に入りプロジェクトだったから。 まだ建築中だったので、火災警報がなったり、 いろいろ妙なことがありました。そしてトイレの近くの壁に「 何に使うのかわからない」ボタンがありました。 何の表示もないし、 勇気を出して押してみても何も起こらないようでした。さて、 このボタンは何だろう? その頃のNewtonプロジェクトの様子はこのように描写されて います。 "Nearly everyone in Newton was working crazy hours at this point; eighty hour weeks were pretty common. While the end wasn’t in sight, we were making good progress on some hard problems. Well, handwriting recognition was still a big bet, and there were a lot of issues around memory footprint and storage, and the development environment was behind, and the language the applications would be written in was still being designed, and PCMCIA card support was rocky, and IR and faxing were flaky, and the built-in applications were still in a lot of flux, not to mention gesture recognition, shape drawing and sound, and battery life, and ROM space, and how we were going to patch ROMs with only a 20K budget of RAM. But aside from those issues, and a few other things (like the schedule), the project was going okay."   Newtonプロジェクトに関わる人は全員狂ったように働い ていた。週80時間労働はあたりまえだった。 まだ終わりは見えなかったがいくつかの困難な問題に関して結構前 進していた。もちろん手書き認識は「賭け」 としかいいようがない状態だったし、メモリや記憶容量のサイズは問題だったし、開発環境は遅れていたし、 アプリを書くための言語はまだ設計されている段階だったし、 PCMCIAカードはバグだらだけだったし、 赤外線とFAXの機能は不安定だったし、 搭載されたアプリはflux(?)だらけだし、 ジェスチャー認識、図形描画、サウンド、バッテリーの持ち、 ROM,RAMのサイズは言うまでもない状態だった。 しかしそうした問題と他の問題(例えばスケジュールとか) を除けばプロジェクトはまあまあうまくいっていた。   さて、 この記事の著者は自宅でLinuxをインストールするためSON Y製のCD-ROMドライブを買いました。 それにはボタンの説明シールが何枚かついていた。 捨てるのはもったいないので、 何枚かポケットに突っ込んで出勤したそうです。 夜の10時か11時頃、 ビルドが終わるのを待ちながらぶらぶらしていた著者の前に例の「 謎のボタン」が目に入りました。 周りに誰もいないのを確かめた後、ポケットから"Eject" シールと矢印シールを取り出して謎のボタンの周りに貼ったのです 。 2-3日もすれば、ビルの管理人かだれかが剥がすだろう、 と思っていたのですが、それから数ヶ月間、「謎のボタン」は「 謎のEjectボタン」として有名になりました。 Ejectというからには押すと何かが「排出」 されるのだろうか。押してみたけど何も起こらない、一体何が「 排出」されるんだろう。謎は深まるばかりでしたが、著者は「 秘密」を守りつづけました。 何ヶ月かが過ぎ、Newtonが出荷されました( 著者の意見では「数ヶ月早すぎた」とのこと) 一年後に著者はAppleを去り別の会社で働き始めました。 Newtonは成功せず、スカリーが去り、ジョブスが復帰し、 iPhoneが発表され、、 そして先日著者のもとにある写真が届いたそうです。それは「 謎のEjectボタン」の写真でした。 20年たった今でもそこにあるらしいのです。 著者はこう述べています。 I wonder if Steve ever pressed that button, or wondered what it did?   Steve (ジョブス)はこのボタンを押して、「何をするんだろう?」 と考えたことがあっただろうか?   --------------- Newtonを「時代に先駆けすぎていた」 というのは簡単ですが、 個人的にはその失敗から多くのことを学ぶことができると思います 。 G.M.ワインバーグの「コンサルタントの秘密」 という名著にこういう言葉があります。 エドセルの訓令 新しいものとつき合わなければならないときは、 二つではなく一つにしよう。 上記文章の中で述べられているように、 Newtonは開発言語と、 それで動くアプリケーションを同時に新規開発しようとしました。 そして私はそれが技術的に彼らが失敗した原因の一つだと思ってい ます。もっともエンジニアの端くれとして「 何もかも新しくしたい」という要求は理解できるところですが、「 エドセルの訓令」はほとんどの場合冷酷に立ちはだかります。 UIのデザインで冒険するなら、 開発環境は実績のあるものを使う。 新しいシステムを基盤にするなら、 アプリケーションは堅実なものを選ぶ。 どちらか一つにしない限りプロジェクトが失敗する可能性は高くなる。 iPhoneの開発もNewtonに負けず劣らず大きな賭けだっ たようです。しかし少なくともアプリを作成するための言語、 Objective- Cは長く使われていて実績があったわけです。 それでも彼らの困難は想像を絶するものだったようですが、 その物語については別の機会に。
大坪と申します。先日10/26に行われました楽天テクノロジーカンファレンスに参加しました。主たる目的はライトニングトークでiPadアプリ「 へやくる! 」について話すことだったのですが、いろいろ興味深い発表をいくつも聞くことができました。   本日はその中からまつもとひろゆき氏の講演について概要を紹介をしたいと思います。ちなみに本来基調講演は楽天の三木谷会長が行う予定だったのですが、「急遽」まつもと氏が講演することになったとのこと。そんな事情を話し、笑いをとるところから講演が始まりました。(個人的にはこの「急遽登板」は歓迎するところでしたが)     -------------- 1990年に学校を卒業し働き始めた。そのころはエンタープライズソフトの開発をしていた。一つのシステムを3年かけ、ウォーターフォールで開発する時代だった。その時から「この開発方法は何かが間違っている」と思ったが何が間違っているかを説明できなかった。   その頃のソフトウェア開発は間違った前提に基づいていた。 ・私たちは何を作ろうとしているかわかっている ・私たちは何が欲しいかわかっている ・状況は変化しない   しかしこれらの前提は馬鹿げている。実際台風がくるとか地震がくるとか誰が予測できるというのか。(注:このカンファレンス自体、台風が接近した場合には中止になりかねませんでした)   このように前提が間違っていたが、当時の企業はどういう戦略を選べたか?   一番目の方法はダチョウのように、頭を穴に突っ込み状況が好転するのを待つこと。   二番目の方法は保守的戦略。「過去から学ぶ」ことである。たとえば人間性は聖書の時代から変わっていないから多くの分野では過去から学ぶことができる。しかしIT業界ではそうはいかない。不思議の国のアリスで赤の女王が言った「同じ場所にいるためには、必死に走らなくちゃ」というのがIT業界にも当てはまる。   3番目の方法は「おとぎの世界に住む」こと。「間違った前提に基づいて進む」ことだが、これは「穴に頭を突っ込んだダチョウ」より悪い戦略だ。   20年前、日本の会社はどのような戦略をとったか?多くの会社は3番目を選んだがこれは間違いだった。   今から考えて、唯一とるべき方法はTry and Error。この戦略を機能させる為には何度も挑戦できなくてはならない。   20年前ソフトウェア開発にはものすごいコストがかかった。それ故Try and Errorは選択しにくかった。最優先は「失敗しないこと」であり、その戦略にプロセスが最適化した。その結果ユーザ満足度は犠牲にされた。   時代は変わり、ソフトウェア開発のコストは劇的に下がった。Network,ツール,プログラミング言語全てがよくなった。今はSocial Codingが可能。   20年前は、とにかくソフトウェアを作る事がゴールだった。すごい金をかけてシステム構築すれば、それだけで差別化できた。しかし今のゴールは「すごいソフトウェア」を作る事。ただソフトウェアを持っているだけでは差別化できない。   ではどうやって「すごいソフトウェア」をつくれるか?   その方法はもちろんわからない。しかしまず無知を自覚する必要がある。ここで機能する唯一の戦略はTry and Errorだ。そして失敗したときのコストを最小化する必要がある。そのためにはOpen source Softwareを使うべきだ。巨人の肩にのって開発をすることで、コストを下げることができる。   コアコンピタンス以外のすべてをOpen Sourceにするべきだ。そしてオープンな開発コミュニティを作り、プロジェクトを最小化する。それによりプロジェクト「組織」を解体し、ソフトウェアが万人にとって開かれるようにすべきだ。   Rubyは元々個人的な興味から作り出した(ペットプロジェクトと言っていました)が、今や世界中で多くの人が使っている。このように将来を見通すことはできない。   唯一の方法は進み続けること。サメのように進み続けなければ死ぬ。Open source projectで偉大なソフトウェアを作るためには開発コミュニティが多くの人を引きつけなくてはならない。そしてできるだけ早く走り、何度も試し、失敗したら早めに撤退すること。 ソフトウェア開発に関する大きな図式を変換する必要がある。「失敗したら終わり」から"失敗もOK"にすべきだ。(もちろん大失敗をしないという条件下でだが) -------- 以上が講演内容。その後何件かQ&Aがありました。私が特に興味深いと思ったのは以下のやりとり   Q:"失敗もOK"という風に会社のカルチャーをかえられるか?多くの会社ではなかなかそうはいかないが。   A:基本的にはそんな会社は辞めるべきだと思う。多くの大きな日本のIT企業は歯車を求めている。従業員に敬意を払っていない。エンジニアの動機の一つは自由度があることであり、自由を得るためにはリーダーになる必要がある。だから歯車を求めているような会社は辞めるべきだ。 ------- 特に最後の言葉は、「そういわれても..」とも思う反面、多くのエンジニアの心に響いたのではないでしょうか。   基本設計-詳細設計-コーディング-試験という整然としたウォーターフロープロセスの間に多くの書類が作られ、レビューが行われ,,という開発に携わった人は多いと思いますし、「何かが間違っている」と感じた人も多いと思います。その「何か」についてまつもと氏の意見を聞けたことは貴重な体験でした。   ちなみに昨年のテクノロジーカンファレンスでは、Design Thinkingというセッションでウォーターフォール型開発についてこんな話がありました。   ノードストロームではウォーターフォール式に開発段階でのチェックを厳格に行った。その結果、全てのinnovationが消えてしまった。ウォーターフォール式の開発がうまくいかない、ということは誰もが知っている。しかしいつまでもそれがなくならないことについて考える必要がある。つまりメリットがあるわけだ。ウォーターフローの利点は、個人にとって「安全」であること。うまく行かなかった時に、「自分じゃない誰か」を非難できる。ソフトウェアの開発が遅れたのは、仕様が出るのが遅れたからだ、とか、無茶苦茶な仕様がでてきたからだ、と非難できる。しかし製品を作るのには向いていない。   来年のテクノロジーカンファレンスではどんな話が聞けるか今から楽しみです。    
ごあいさつ
皆様はじめまして。 不動産ポータルサイト「HOME'S」を運営しています株式会社ネクストの秋山と申します。   この度、ネクストからのエンジニアによる技術情報を中心とした情報発信を行っていくこととなりました。 弊社は基本的に内製で開発を行っており、そこで得られた様々なナレッジや技術、活動をお伝えしていこうと思います。   本ブログで主にお伝えする内容を下記に列挙します。 サービス開発、運用時に得た知識や技術 R&D の進捗や得た知識 弊社エンジニアの日常や新しい取り組み 新サービスやイベントの告知   株式会社ネクストのメイン事業である不動産ポータルサイト「HOME'S」をメインに、 弊社エンジニアの活動を通して有益な情報や活動をお伝えすることで、 株式会社ネクストを知っていただくと共に、皆様に少しでもお役に立てる情報を 発信してまいります。   今後とも、「株式会社ネクスト エンジニアBlog」を宜しくお願い致します。
こんにちは、ネクスト 清田です。 前回の記事 では、情報検索システムの研究で評価に使われてきた「再現率」「精度」という指標について紹介するとともに、現実の情報検索システムでは、システムの「ユーザー」を巻き込まないと本当の姿は分からないことを示しました。今回は、ユーザーを巻き込んでシステムを評価するには、どんなことを考えておく必要があるのかについて紹介します。 システムとユーザー、どちらにフォーカスを当てるのか? 前回の記事では、情報検索システムを研究するには、システムを「入力と出力を持つブラックボックス」として扱うやり方と、システムとユーザーをセットにして1つの系として扱うやり方の、2つの方法があることを示しました。しかし、この2つの方法は完全に区別できるものではなく、場合に応じて両者を組み合わせて利用されます。 情報検索システムの評価を専門としているノース カロライナ大学のDiane Kelly博士は、著書「Methods for Evaluating Interactive Information Retrieval Systems with Users *1 」の中で、情報検索システムの2つの研究の方法の間には、図1に示すようなさまざまなバリエーションが連続体として存在することを示しています。この図について簡単に説明しましょう。 図1. 情報検索システムの研究アプローチの連続体 (Diane Kelly 「Methods for Evaluating Interactive Information Retrieval Systems with Users」 p.10 より引用) 左側に行けば行くほどシステム側に寄った研究、右側に行けば行くほどユーザー(人間)側に寄った研究になっています。最も左側にある「TRECスタイルの研究」は、TREC(Text REtrieval Conference)という情報検索研究ワークショップで主流とされてきた研究のやり方、つまり情報検索システムをブラックボックスとして扱い、あらかじめ準備されている正解データを使って、再現率と精度という指標で性能を測定するというタイプの研究です。定量的な評価が可能なので、異なるシステム同士の比較も簡単ですし、工学的アプローチによって研究を進めることができます。一方、最も右側にある「コンテキストの中での情報探索の挙動」は、完全にユーザーにフォーカスを絞って、ユーザーの情報要求とふるまいの関係を明らかにしようとするタイプの研究です。そこでは標準的な評価手法が確立されていないため、定量的な評価手法が適用できず、定性的な評価によらざるを得ません。 現実には、これらの極端なケースで研究が行われることはあまりなく、多くの情報検索システムの研究は両者の中間のやり方で行われます。 例えば、図1の左から2番目の「ユーザーによる適合性評価」は、再現率・精度の指標は用いるものの、正解データはあらかじめ準備されてはおらず、研究者自身がユーザーを募集することによって、正解データなどの評価に必要な基盤を作っていくタイプの研究です。他の研究者が扱っていない新しいタイプの情報検索システムの研究に取り組むには、正解データを自分で作る必要があるため、ある程度ユーザーを巻き込んでいく必要が出てきます。 左から4番目の「ログ解析」は、ユーザーが情報検索システムを利用する際に収集できるログデータを用いるタイプの研究です。大規模なWeb情報サービスが普及するにつれて、ログ解析による研究が非常に盛んになってきています。例えば、新しいユーザー インタフェースやアルゴリズムを試したい場合、その潜在的な効果を測る場合にログ解析はきわめて強力な手法です。 真ん中の「TRECインタラクティブ研究」は、インタラクティブ情報検索システムを対象とした研究で、最近のTRECでは研究が非常に盛んになってきています。このタイプの研究は、主にユーザーに直接関連した機能を研究の対象としていて、さまざまなデータ収集手法を用いてユーザビリティを評価します。また、ユーザーへのインタビューによって質的評価を得ることも行われます。 情報検索システム評価の歴史 歴史的にみると、情報検索システムの研究は、工学的アプローチを適用できる図1の左側に寄った研究アプローチが中心でした。情報検索システムがユーザーの存在を前提としている以上、ユーザーを巻き込んだ評価をする必要があることは、1970年代にはすでに認識されていました。しかし、確立された評価手法が存在しなかったため、少数の探索的研究を除いてユーザーの方に重きを置いた評価は行われてきませんでした。1990年代中頃までは、情報検索システムはある程度訓練を受けたユーザーのみを対象としていたため、典型的なユーザー像を想定しやすいという理由もありました。 しかし、Webベースの情報検索システムが広く普及するにつれて、さまざまなレベルのユーザーによってシステムが利用されるようになってきたため、ユーザーを巻き込んで評価を行うための手法を確立する努力が本格的に始められました。現在は、大規模なWebサービスを展開する企業などを中心に、ユーザーを巻き込んださまざまな評価が行われるようになってきています。 上記で紹介したKelly博士の著書では、ユーザーを巻き込んだ評価手法が体系的にまとめられており、非常に参考になります。現在、筑波大学の上保秀夫先生を中心として本書の邦訳を出版する準備が進められており、今年秋頃には出版される予定です(清田も一部の翻訳を担当させていただいています)。興味をお持ちの方はぜひチェックしてみてください。 情報検索システムを「科学」するということ これまで述べてきたように、情報検索システムの研究には「システム」と「ユーザー」という2つの極があって、歴史的には「システム」側からスタートして徐々に「ユーザー」側に中心が移ってくるという変遷を経てきました。エンジニアの立場から見ると、図1の「システム」側の単純化されたアプローチの方が馴染みがあります。「ユーザー」を巻き込んで評価を行うことは、問題を非常に複雑にしてしまうことから、これまではあえて避けられてきたと考えられます。しかし、今日のように情報検索システムが広く使われるようになった状況では、「ユーザー」を巻き込むことはむしろ必然的に求められるようになってきています。情報検索システムの本来の姿である「システムとユーザーの組み合わせ」を研究対象として扱うことは、複雑な問題の本質に迫るという大きなチャレンジです。工学という枠組みを脱皮して、複雑な問題の本質に迫ること、つまり「科学」という枠組みで考えることが求められます。 20世紀からの科学の発展は、「統計」という強力なツールの存在なくしては語れません。情報検索システムの研究分野でも、恣意的な要素がどうしても入ってしまう「少数のユーザーによる評価実験」や「あらかじめ準備されているクエリーと正解データの組み合わせ」というアプローチから脱皮して、「ユーザーのサンプリング」や、「実験結果の検定」などといった統計的アプローチが利用されるようになってきています。たとえば、新しいアルゴリズムの有用性を知りたいときには、一部のユーザーを無作為抽出して、元々のアルゴリズムを使っているユーザーとの違いを調べるという方法(A/Bテストと呼ばれます)がよく利用されますが、いったい何人くらいのユーザーを抽出すれば良いのかを知るには、基本的な統計的検定の知識が役に立ちます。また、情報検索システムのクエリーログデータの分析に利用されている手法の多くは、統計的アプローチそのものです。 次回の記事 では、評価の対象とするユーザーを選ぶ場合に気を付けておく必要があることについて紹介しています。 記事のリスト ユーザー向け情報サービスの「評価」を考える (第1回) - 株式会社ネクスト エンジニアBlog ユーザー向け情報サービスの「評価」を考える (第3回) - 株式会社ネクスト エンジニアBlog *1 : Methods for Evaluating Interactive Information Retrieval Systems and Users (Foundations and Trends(r) in Information Retrieval) 作者: Diane Kelly 出版社/メーカー: Now Publishers 発売日: 2009/04/30 メディア: ペーパーバック 購入 : 1人 クリック : 6回 この商品を含むブログを見る
こんにちは、株式会社ネクスト 清田です。 ネクストでは、HOME'Sをはじめとしてネクストが提供する情報サービスに新たな価値を加えるため、膨大なデータベースから最適な情報を提示する検索技術や、潜在的なニーズを抽出して一人ひとりにぴったりな情報を提供するレコメンデーション エンジンの研究開発に日々取り組んでいます。このブログでも、オープン ソースの全文検索エンジンSolrの拡張などの活動を紹介してきています。 今回は、情報検索やレコメンデーションの研究を行う上でどうしても避けられない、「評価」にまつわる話題を紹介します。 情報検索システムの評価とは? 情報検索システムの研究には長い歴史があり、研究の「評価」に使われる指標が確立しています。情報検索システムの研究で最も良く使われるのは、「再現率」と「精度」という2つの指標です。「再現率」(recall)とは「ユーザーのニーズに合った情報のうち、検索にヒットした情報の割合」、「精度」(precision)とは「検索にヒットした情報のうち、ユーザーのニーズに合った情報の割合」のことです。 近所の図書館で「プログラミング言語の学習」について書かれた本を借りる場合を例にとって説明します。この図書館では、「プログラミング言語の学習」について書かれた本が10冊あります。あなたが図書館のロビーに置いてある検索端末で「プログラミング言語」というキーワードで検索すると、20冊の本がヒットしたとします。ヒットした20冊の中には、あなたが探していた「プログラミング言語の学習」についての本は8冊あり、その他の12冊は、「プログラミング言語の開発」や「プログラミング言語の歴史」など、あなたが探しているものとは違う本だったとしましょう。 再現率は、あなたが探していてかつ見つかった8冊を、図書館の中であなたが探している本すべての冊数10冊で割ったもの、すなわち80%です。いっぽう精度は、あなたが探していてかつ見つかった8冊を、ヒットしたすべての本の冊数20冊で割ったもの、すなわち40%です。再現率と精度は、100%に近ければ近いほど「良い検索システム」であるということになります。理想の検索システムは再現率、精度ともに100%のシステムということになりますが、多くの場合、再現率と精度はトレード オフの関係にあります。 例えば、具体的なプログラミング言語の名前(C、JavaやPython)を検索キーワードとして自動的に追加すれば、再現率を上げることができますが、多くの場合は精度が下がってしまいます(「ベンツCクラス」や、「ジャワ(Java)島」、「モンティ・パイソン」についての本もヒットしてしまうから)。逆に精度を上げようとすれば再現率は下がってしまいます。このトレードオフの存在を前提に、できるだけ再現率・精度ともに高くしようという努力の積み重ねが、今日の検索エンジン技術の土台となっています。 現実の情報検索システムの評価にまつわる問題 しかし、再現率や精度という指標は、Webの爆発的な普及にともなって出現してきた新しいタイプの情報検索システムには、必ずしも使えなくなってきています。たとえばHOME'Sでは、日本全国の300万件以上の物件データベースの中からあなたが探したい物件を自由に検索することができますが、そもそも「あなたが探したい物件」が300万件のデータベースの中に何件含まれるのかを知ることは不可能です。ある物件があなたの本当に探しているものかどうかは、HOME'Sで探して不動産会社さんを訪問し、物件を見学することによってはじめて分かります(実際にはしばらく住んでみないと分からないこともあるでしょう)。全国の300万件の物件をすべて見学することは一生かけても無理ですし、探す地域を限っていたとしても、そのエリアのすべての物件を調べるほどの時間はとれないことが多いでしょう。 図1. 工学的アプローチによる情報検索システム研究のモデル 情報検索システムには、評価を難しくする本質的な課題があります。再現率・適合率を測るためには、図1に示すように、情報検索システムを「入力」と「出力」を持つブラックボックスとしてとらえ、どの「入力」に対してどのような「出力」が正しいのかを決めておく必要があります。しかし、現実の情報検索システムには必ず「人間」がユーザーとして関わっています。例えばHOME'Sというサービスは、皆さん一人ひとりの「家探し」というニーズの上に成り立っています。そのため、情報検索システムを評価するためには、必ずユーザーとしての人間を巻き込まなければなりません(図2)。 図2. 現実世界の情報検索システムのモデル 次回の記事 では、情報検索システムの評価に人間を巻き込むこと、すなわちユーザベースの評価をする際に、何を考える必要があるかについて紹介します。 記事のリスト ユーザー向け情報サービスの「評価」を考える (第2回) - 株式会社ネクスト エンジニアBlog ユーザー向け情報サービスの「評価」を考える (第3回) - 株式会社ネクスト エンジニアBlog
初めまして、ネクストでデータマイニング・レコメンドを担当している古川と申します。本ブログでは、日々の業務でお世話になっている、アルゴリズムやソフトウェアなどを紹介していきたいと思います。 まず第 1 弾として、全文検索エンジン Solr の検索 plugin 作成方法について数回の連載に分けて紹介します。 導入 Solr は、 日本語の全文検索が可能である RDMBS 同様、フィールド値を使ったソート、複数フィールドを使った高速なソートが可能である 分散処理機能を備え、スケーラビリティに優れている キャッシュ機能を備え高速なレスポンスが可能である レプリケーション可能である オンラインでのデータ更新がサポートされている などの多くの利点を持つオープンソースの全文検索エンジンで、様々なサイトでの利用が広がっています。 課題 デフォルト機能でも多くのニーズに答えてくれる Solr ですが、サイト固有のニーズを実現するためには、多少のカスタマイズが必要になることがあります。 HOME'S でのニーズを例に説明します。 フィールド値として家賃、面積などを保存しておけば、「家賃が 8 万円以下」という検索条件について、合致する物件一覧を、「家賃が安い順-面積の広い順」、「家賃が高い順 - 面積広い順」といった順序でソートすることは、 RDBMS 同様 Solr でも可能です。しかしながら、これでは「家賃がそこそこの割に、面積もそこそこ広い」という条件の良い物件があったとしても検索結果上位に表示されにくい、という問題があります。もちろん、ユーザにより詳細な検索条件を入力して頂ければ解決する問題ですが、 1 度目の検索でサイトを離脱されてしまうユーザも多く、少ない入力条件でいかに“いい感じ”の検索結果を返すかということは、非常に重要な課題です。 “いい感じ” の結果を返すためには、「家賃、面積」という2つのフィールドに関して、個々に考えるのではなく、2つのフィールドを総合的に加味する必要があります。しかし、これを Solr のデフォルト機能で実現するのは難しく、 plugin を作成ました。 Lucene での実現方法 本連載では、検索クエリと複数フィールド値を加味しながらソート順を決定する単純なサンプルプログラムを例に、作成方法を説明していきたいと思います。 連載 1 回目の今回は、 Solr 用の検索 plugin 作成をする前段階として、 Solr の使っている Lucene ライブラリでの実現方法を紹介します。なお、実現方法に関しては、 株式会社ロンウイット の関口さんが、 ブログで紹介されている方法 のうち、 Collector クラスを継承して実現する方法を参考にさせて頂きました。 以下のようにフィールドが id 、 type 、 x 、 y からなるドキュメントセットについて、検索条件として、 type と x 、 y を指定し、同じ type のドキュメントをスコアの小さい順に取得するという検索を考えます。このとき、 Score = weight(type)(qx‐x)(qx-x) + (1-weight(type)) (qy-y)(qy-y) のように、typeによって x 、 y の weight が変わる値を検索用スコアとして考えました。 source.tsv #id type x y 1 a 10 20 2 b 10 20 3 a 11 11 4 b 11 11 次のプログラムは、上記 TSV ファイルを読み込み、検索用のインデックス作成、プログラム内で指定した条件で検索を行うというプログラムです。 BlogTest.java package blog; import java.util.HashMap; import java.util.ArrayList; import java.io.FileReader; import java.io.BufferedReader; import java.io.FileWriter; import java.io.BufferedWriter; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.NumericField; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.Term; import org.apache.lucene.analysis.SimpleAnalyzer; import org.apache.lucene.util.Version; import org.apache.lucene.store.Directory; import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.FieldCache; public class BlogTest { public static void main(String[] args) { String srcPath = args[ 0 ]; Directory directory = new RAMDirectory(); System.out.println( "[create index]" ); int numDocuments = createIndex(directory, srcPath); System.out.println( " \t loaded " + numDocuments + " documents" ); System.out.println( "[run test]" ); BlogTestQuery[] testQueries = new BlogTestQuery[ 2 ]; testQueries[ 0 ] = new BlogTestQuery( "a" , 10 , 10 ); testQueries[ 1 ] = new BlogTestQuery( "b" , 10 , 10 ); searchTest(directory, testQueries, 5 ); System.out.println( "[done]" ); } public static int createIndex(Directory directory, String path) { int counter = 0 ; try { IndexWriterConfig config = new IndexWriterConfig( Version.LUCENE_34, new SimpleAnalyzer(Version.LUCENE_34)); IndexWriter iw = new IndexWriter(directory, config); FileReader fr = new FileReader(path); BufferedReader br = new BufferedReader(fr); String strLine; String[] buf; while ( (strLine = br.readLine()) != null ) { if (strLine.charAt( 0 ) == '#' ) continue ; buf = strLine.split( " \\ t" ); if ( buf.length != 4 ) continue ; counter += 1 ; String id = buf[ 0 ]; String type = buf[ 1 ]; int x = Integer.parseInt(buf[ 2 ]); int y = Integer.parseInt(buf[ 3 ]); Document doc = new Document(); doc.add( new Field( "id" , id, Field.Store.YES, Field.Index.NOT_ANALYZED)); doc.add( new Field( "type" , type, Field.Store.YES, Field.Index.NOT_ANALYZED)); doc.add( new NumericField( "x" , Field.Store.YES, true ).setIntValue(x)); doc.add( new NumericField( "y" , Field.Store.YES, true ).setIntValue(y)); iw.addDocument(doc); } br.close(); fr.close(); iw.close(); } catch (Exception e) { e.printStackTrace(); System.exit( 1 ); } return counter; } public static void searchTest(Directory directory, BlogTestQuery[] testQueries, int numLimit) { // searcher作成 System.out.print( " \t initializing searcher..." ); IndexSearcher is = null ; IndexReader ir = null ; HashMap<String, Double> weights = new HashMap<String, Double>( 2 ); weights.put( "a" , 0.5 ); weights.put( "b" , 0.999 ); int [][] caches = new int [ 2 ][]; try { is = new IndexSearcher(directory, true ); ir = is.getIndexReader(); caches[ 0 ] = FieldCache.DEFAULT.getInts(ir, "x" ); caches[ 1 ] = FieldCache.DEFAULT.getInts(ir, "y" ); } catch (Exception e) { e.printStackTrace(); System.exit( 1 ); } System.out.println( " done." ); // run search for ( int i= 0 ; i<testQueries.length; i++) { BlogTestQuery testQuery = testQueries[i]; Query query = new TermQuery( new Term( "type" , testQuery.type)); double weight = weights.get(testQuery.type); BlogTestCollector collector = new BlogTestCollector(ir, caches, weight, testQuery.x, testQuery.y); try { is.search(query, null , collector); } catch (Exception e) { e.printStackTrace(); System.exit( 1 ); } ArrayList<DocIdScore> docIdScores = collector.getDocIdScores(numLimit); System.out.print( " \t type=" + testQuery.type + " \t x=" + testQuery.x + " \t y=" + testQuery.y); System.out.println( " \t weight=" + weight); System.out.println( " \t\t id \t type \t x \t y \t score" ); for ( int j= 0 ; j<docIdScores.size(); j++){ int id = (docIdScores.get(j)).getDocumentId(); double score = (docIdScores.get(j)).getScore(); try { Document doc = is.doc(id); System.out.print( " \t\t " ); System.out.print(doc.get( "id" )+ " \t " ); System.out.print(doc.get( "type" )+ " \t " ); System.out.print(doc.get( "x" )+ " \t " ); System.out.print(doc.get( "y" )+ " \t " ); System.out.print(score+ " \n " ); } catch (Exception e) { e.printStackTrace(); System.exit( 1 ); } } } } } BlogTestCollector.java package blog; import java.lang.Math; import java.util.Collections; import java.util.ArrayList; import java.util.Comparator; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.Collector; import org.apache.lucene.search.Scorer; import org.apache.lucene.search.FieldCache; public class BlogTestCollector extends Collector { private ArrayList<DocIdScore> docIdScores; private IndexReader reader; private int docBase; private int [][] fieldsCaches; private double weight; private int x; private int y; public BlogTestCollector(IndexReader reader, int [][] caches, double weight, int x, int y) { this .reader = reader; this .docIdScores = new ArrayList<DocIdScore>(); try { this .fieldsCaches = caches; this .weight = weight; this .x = x; this .y = y; } catch (Exception e) { e.printStackTrace(); System.exit( 1 ); } } @Override public void setNextReader(IndexReader reader , int docBase) { this .docBase = docBase; } @Override public void setScorer(Scorer socorer){} @Override public boolean acceptsDocsOutOfOrder(){ return true ; } @Override public void collect( int id) { try { int docid = this .docBase + id; int x = this .fieldsCaches[ 0 ][docid]; int y = this .fieldsCaches[ 1 ][docid]; double score = 0 ; score = this .weight*( this .x - x)*( this .x - x) + ( 1.0 - this .weight)*( this .y - y)*( this .y - y); this .docIdScores.add( new DocIdScore( docid, score )); } catch (Exception e) { e.printStackTrace(); System.exit( 1 ); } } public ArrayList<DocIdScore> getDocIdScores( int limit) { Collections.sort( this .docIdScores, new ScoreComparator() ); ArrayList<DocIdScore> retvals = new ArrayList<DocIdScore>(limit); int maxSize = this .docIdScores.size(); if ( limit > maxSize ) { limit = maxSize; } for ( int i= 0 ; i<limit; i++) { retvals.add( this .docIdScores.get(i)); } return retvals; } static class ScoreComparator implements Comparator<DocIdScore> { public int compare( DocIdScore a, DocIdScore b) { double a_score = a.getScore(); double b_score = b.getScore(); if ( a_score > b_score ) { return 1 ; } else if (a_score < b_score) { return - 1 ; } else { return 0 ; } } } } BlogTestQuery.java package blog; class BlogTestQuery { public String type; public int x; public int y; public BlogTestQuery(String type, int x, int y) { this .type = type; this .x = x; this .y = y; } } DocidScore.java package blog; class DocIdScore { private int id; private double score; public DocIdScore( int id, double score) { this .id = id; this .score = score; } public int getDocumentId() { return this .id; } public double getScore() { return this .score; } } インデックス作成部分、クエリ指定部分など Lucene の基本的な使い方については、参考文献を参照して頂くとして、今回のポイントは、 BlogTest.java 115 行目の、BlogTestCollector オブジェクトを作成し、 IndexSearcher オブジェクトの search() 関数の引数として渡し検索を行う部分 BlogTest.java 120 行目の、BlogTestCollector オブジェクトの getDocIdScores() 関数で検索結果を取得する部分 になります。 1 のように、 search() 関数の引数として BlogTestCollector オブジェクトを渡すことで、 IndexSearcher の search() 関数が条件に合致するドキュメントを発見するたびに、 BlogTestCollector の collect() 関数が実行され、 BlogTestCollector 内部にスコア計算結果が保存されていきます。そして、 2 でその結果一覧をスコア順にソートして取得しているわけです。 collect() 関数、 getDocIdScores() 関数の詳細は、ソースコードをご覧ください。 プログラムの実行結果は、以下の通りです。検索クエリの type の値により、 weight が変更され、 x 、 y が同じ値であっても、異なるソート結果となっていることが分かります。 [create index] loaded 4 documents [run test] initializing searcher... done. type=a x=10 y=10 weight=0.5 id type x y score 3 a 11 11 1.0 1 a 10 20 50.0 type=b x=10 y=10 weight=0.999 id type x y score 2 b 10 20 0.1 4 b 11 11 1.0 Lucene のみで実現しているシステムの場合、検索部分を今回の例のように変更するだけで、ソート順を思いのままに変更できますが、 Solr へ組み込み時には、更に Solr の検索コンポーネントの作法に合わせる必要があります。次回は、その方法に関して解説します。 参考文献 Apache Lucene 入門 ~Java・オープンソース・全文検索システムの構築 作者: 関口宏司 出版社/メーカー: 技術評論社 発売日: 2006/05/17 メディア: 大型本 購入 : 5人 クリック : 156回 この商品を含むブログ (30件) を見る Lucene in Action 作者: Michael McCandless,Erik Hatcher,Otis Gospodnetic 出版社/メーカー: Manning Pubns Co 発売日: 2010/06/30 メディア: ペーパーバック 購入 : 1人 クリック : 10回 この商品を含むブログ (3件) を見る Apache Solr入門 ―オープンソース全文検索エンジン 作者: 関口宏司,三部靖夫,武田光平,中野猛,大谷純 出版社/メーカー: 技術評論社 発売日: 2010/02/20 メディア: 大型本 購入 : 18人 クリック : 567回 この商品を含むブログ (22件) を見る