はじめてのUnity (C#)

参考: http://catlikecoding.com/unity/tutorials/clock/

シンプルな時計を作る

完成品はこちら:

unity-clock

まずはプロジェクトの作成:

プロジェクトの作成が完了したら、Create > Create Emptyから新しく"Clock"という名でゲームオブジェクトを作成する。また、Create > Create Empty Child を選択し、Clockの子オブジェクトとしてHours, Minutes, Secondsを作成:

次に、時計の針を作るために、それぞれの子オブジェクトに対し、子cubeを作成する。それぞれのポジションとスケールは次の通り:

position scale
Hours (0,1,0) (0.5, 2, 0.5)
Minuts (0, 1.5, 0) (0.25, 3, 0.25)
Seconds (0, 2, 0) (0.1, 4, 0.1)

オブジェクトのヒエラルキーと現時点でのSceneは次のようになる:

アニメーションの追加

時計を動かすにはスクリプトが必要だ。まずは、ProjectタブからCreate > C# Scriptで新しくC# スクリプトを作成し、ClockAnimatorと名前をつける。スクリプト編集するために、ファイルをエディタで開こう。

まず、UnityEngineの名前空間から必要な物を使用することを宣言する。次に、ClockAnimatorクラスにMonoBehaviourからの継承を宣言する。

using UnityEngine;

public class ClockAnimator : MonoBehaviour {
}

ClockオブジェクトのInspectorからAdd Component > Scripts > ClockAnimatorと選択して、オブジェクトとスクリプトをつなげる。

時計の針を動かすためには、Transformコンポーネントにエディタからアクセスすることが必要になるので、Transform変数を宣言し、オブジェクトのプロパティとしてそれぞれに割り当てる。

using UnityEngine;

public class ClockAnimator : MonoBehaviour {

    public Transform hours, minutes, seconds;
}

次に、Updateメソッドを追加する。このメソッドはフレーム毎に呼び出される特殊なメソッドだ。

using UnityEngine;

public class ClockAnimator : MonoBehaviour {

    public Transform hours, minutes, seconds;

    private void Update () {

    }
}

このメソッドを追加すると、コンポーネントにチェックボックスが表示され、オン・オフが設定できる。

Hoursの針は1時間毎に360/12度、Minutesの針は1分ごとに360/60度、1秒ごとに360/60度回転する。これらの値を定数の浮動小数点として次のように定義する。

using UnityEngine;

public class ClockAnimator : MonoBehaviour {

    private const float
        hoursToDegrees = 360f / 12f,
        minutesToDegrees = 360f / 60f,
        secondsToDegrees = 360f / 60f;

    public Transform hours, minutes, seconds;

    private void Update () {

    }
}

System名前空間から、現在の時刻を知るため、DateTimeNowプロパティを呼び出し、time変数に格納する。

using UnityEngine;
using System;

public class ClockAnimator : MonoBehaviour {

    private const float
        hoursToDegrees = 360f / 12f,
        minutesToDegrees = 360f / 60f,
        secondsToDegrees = 360f / 60f;

    public Transform hours, minutes, seconds;

    private void Update () {
        DateTime time = DateTime.Now;
    }
}

時計の針を動かすには、hoursminutessecondsのコンポーネントのlocalRotationを直接変更する必要がある。Quaternionのあるメソッドを使って、任意の回転を定義しよう。

using UnityEngine;
using System;

public class ClockAnimator : MonoBehaviour {

    private const float
        hoursToDegrees = 360f / 12f,
        minutesToDegrees = 360f / 60f,
        secondsToDegrees = 360f / 60f;

    public Transform hours, minutes, seconds;

    private void Update () {
        DateTime time = DateTime.Now;
        hours.localRotation =
            Quaternion.Euler(0f, 0f, time.Hour * -hoursToDegrees);
        minutes.localRotation =
            Quaternion.Euler(0f, 0f, time.Minute * -minutesToDegrees);
        seconds.localRotation =
            Quaternion.Euler(0f, 0f, time.Second * -secondsToDegrees);
    }
}

真ん中のPlayボタンを押せば、アニメーションが開始され、時計が動き始める。

unity-clock

はじめてのAurelia

http://aurelia.io

Aureliaとは次世代UIフレームワーク。AngularJS、Reactに比べて影が薄い。どうなっていくんだろうか?チュートリアルをやってみた感想としては、HTTPリクエストとルーティング周りがクリーンで素敵。

以下、チュートリアルの適当な和訳。

Getting Set Up

キットをここからダウンロードします。

  • NodeJS - キットのフォルダー内で簡単なサーバーを立ち上げるには、http-servernpm install -g http-serverでインストールします。インストールが終了したら、http-server -o -c-1コマンドでサーバーを立ち上げます。

The Index.html Page

index.htmlは、Aureliaベースのアプリケーションのテンプレートとなるファイルです。

index.html

<!doctype html>
<html>
  <head>
    <title>Aurelia</title>
    <link rel="stylesheet" href="styles/styles.css" />
    <meta name="viewpoint" content="width=device-width, initial-scalse=1"/>
  </head>
  <body aurelia-app>
    <script src="jspm_packages/system.js"></script>
    <script src="config.js"></script>
    <script>
      SystemJS.import('aurelia-boostrapper');
    </script>
  </body>
</html>

ヘッダーはきわめてシンプルで、スタイルシートといくつかのメタデータを指定しています。

スクリプトタグを見てみます。モジュールローダーであるsystem.jsは、Aureliaのライブラリを読み込んでくれます。次に、config.jsファイルはローダーの設定を記述しています。ツールを使ってAureliaのパッケージをインストールすれば、自動的に生成されます。

モジュールローダーとコンフィグを設定した後に、SystemJS.importを使って、aurelia-boostrapperモジュールを読み込んでいます。

このブートストラッパーが読み込まれると、HTMLドキュメントのaurelia-app要素を探します。この要素によってブーストラッパーはアプリケーションのビューモデルとビューを読み込みます。

Creating Your First Screen

Aureliaでは、UIコンポーネントはビューとビューモデルの2つの部分にわかれます。ビューはHTMLで記述されて、そのDOMに描画します。ビューモデルはES 2016で記述されて、データとその振る舞いをビューに渡します。Aureliaの強力なデータバインディングによって、これら2つの部分はリンクし、データの変更が互いに反映されます。この分離のアイデアは、デベロッパー/デザイナーの協業、メンテのしやすさ、アーキテクチャ上の柔軟性、ソースコントロールにおいても、有用です。

srcフォルダーにapp.htmlapp.jsファイルがありますが、これらがビューとビューモデルのコンポーネントです。ビューモデルをfirstNameとlastNameを持つ、シンプルなクラスに置き換えましょう。fullNameプロパティと、"submit"メソッドも追加します。

app.js

export class App {
  heading = 'Welcome to Aurelia';
  firstName = 'John';
  lastName = 'Doe';

  get fullName() {
    return `${this.firstName} ${this.lastName}`;
  }

  submit() {
    alert(`Welcome, ${this.fullName}!`);
  }
}

基本的なデータと振る舞いを記述するビューモデルを作成しました。次にHTMLを使って、ビューを作成しましょう。

app.html

<template>
  <section>
    <h2>${heading}</h2>

    <form submit.trigger="submit()">
      <div>
        <label>First Name</label>
        <input type="text" value.bind="firstName"/>
      </div>
      <div>
        <label>Full Name</label>
        <p>${fullName}</p>
      </div>
      <button type="submit">Submit</button>
    </form>
  </section>
</template>

まず、すべてのビューはtemplateタグにネストされています。ビューは基本的な入力フォームです。入力コントロールを見てみましょう。value.bind="firstName"に気づいたでしょうか?これによって、入力した値とビューモデルのfirstNameプロパティのデータバインディングを行います。ビューモデルのプロパティが変化した時に、入力値は新しい値に更新されます。入力コントロールの値を変更した時、Aureliaはビューモデルに新しい値を入れます。簡単なことですね。

この例では、もっと面白いことが起きています。最後のフォームグループのHTML中に、${fullName}があります。これは文字列の挿入です。ビューモデルからビューへの一方的なデータバインディングで、自動的に文字列へと変換され、ドキュメントに挿入されます。最後に、フォーム要素を見てみましょう。submit.trigger="submit()"に気づくでしょう。これはイベントバインディングで、フォームのsubmitイベントが発火した時に、ビューモデルのsubmitメソッドが呼び出されます。

ブラウザを更新して、確かめてみましょう。

Adding Naviagation

ひとつのページのアプリだと面白くないですよね。スクリーンを幾つか追加して、クライアントサイドのルーティングを設定しましょう。app.jsapp.htmlをそれぞれwelcome.jswelcome.htmlにリネームしましょう。マルチスクリーンアプリの最初のページになります。"layout"、"master page"、"root component"となるapp.jsapp.htmlを新しく作成します。ビューにはナビゲーションUIと現在のスクリーンのコンテントプレースホルダーを、ビューモデルにはルーターのインスタンスを設定しましょう。

app.js

export class App {
  configureRouter(config, router) {
    config.title = 'Aurelia';
    config.map([
      { route: ['', 'welcome'], name: 'welcome', moduleId: './welcome', nav: true, title: 'Welcome' }
    ]);

    this.router = router;
  }
}

ルーターを使うために、configureRouterコールバックを実行するAppクラスをエクスポートします。このコールバックは設定オブジェクトとともに呼びだされます。設定オブジェクトを使用すれば、ドキュメントのタイトルを生成する際に、これを使用し、ルートのマッピングを行います。それぞれのルートは次のプロパティを持ちます:

  • route: パターンに一致したら、このルートにナビゲートします。静的なものだけでなく、customer/:idのようにパラメーターを使用することも出来ます。ワイルドカードパターンとクエリ文字列もサポートしています。routeは文字列パターンやパターンの配列でなければなりません。
  • name: URLを生成するコードで使用する名前です。
  • moduleId: このルートで描画するコンポーネントのパスです。
  • title: ドキュメントのタイトルを任意で指定できます。
  • nav: このルートがナビゲーションモジュールに含まれる場合、trueに設定します。

app.html

<template>
  <require from="bootstrap/css/bootstrap.css"></require>
  <require from="font-awesome/css/font-awesome.css"></require>

  <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
        <span class="sr-only">Toggle Navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">
        <i class="fa fa-home"></i>
        <span>${router.title}</span>
      </a>
    </div>

    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
          <a href.bind="row.href">${row.title}</a>
        </li>
      </ul>

      <ul class="nav navbar-nav navbar-right">
        <li class="loader" if.bind="router.isNavigating">
          <i class="fa fa-spinner fa-spin fa-2x"></i>
        </li>
      </ul>
    </div>
  </nav>

  <div class="page-host">
    <router-view></router-view>
  </div>
</template>

Appクラスは上記のapp.htmlのビューにデータバインドされます。このマークアップの大部分はメインのナビゲーション構造を扱います。ビューの上部にrequireエレメントがありますね。ES2015/2016のimportと同じく、AureliaはHTMLのrequireエレメントの使用を可能にします。このエレメントによって、この場合ではCSSを読み込むことが出来ます。これで、ナビゲーション構造のレイアウトにブートストラップを使用することが出来ます。

基本的なデータバインディングと文字列の挿入はすでに見たので、新しいことに注目しましょう。ulエレメントのnavbar-navを見て下さい。lirepeat.for="row of rotuer.navigation"によって、どのようにリピーターを使うかを示しています。これによって、router.navigation配列のそれぞれのアイテムにliが作られます。ローカル変数はrowで、子エレメントで見ることが出来ます。

liでは、クラスを追加/削除するために、どのように文字列挿入を使用しているか示しています。下に行くと、ふたつ目のulがあります。子エレメントのliのバインディングを見て下さい。if.bind="router.isNavigating"これは、条件のもとで、バインディングされた値をベースとしたliを追加/削除します。ルーターはisNavigatingプロパティを更新します。

ブラウザを更新して、確かめてみましょう。"welcome"ルートのために選択されたタブが確認できます。このwelcomeビューはメインコンテントエリアで表示されます。ブラウザのデバグツールを開いて、DOMを見てみましょう。router-viewの中でwelcomeビューコンテントが表示されているのがわかるでしょう。

Adding a Second Page

さて、ナビゲーションアプリケーションができましたが、スクリーンがひとつだと面白く無いので、ふたつ目のスクリーンを追加しましょう。

Githubからユーザー情報を取得して表示しましょう。まずは、仮のスクリーンへのルートを設定します。

app.js

export class App {
  configureRouter(config, router){
    config.title = 'Aurelia';
    config.map([
      { route: ['','welcome'],  name: 'welcome',  moduleId: './welcome',  nav: true, title:'Welcome' },
      { route: 'users',         name: 'users',    moduleId: './users',    nav: true, title:'Github Users' }
    ]);

    this.router = router;
  }
}

users.jsusers.htmlファイルを作成する必要があるとわかるでしょう。これがそのソースです。

users.js

import {inject} from 'aurelia-framework';
import {HttpClient} from 'aurelia-fetch-client';
import 'fetch';

@inject(HttpClient)
export class Users {
  heading = 'Github Users';
  users = [];

  constructor(http) {
    http.configure(config => {
      config
        .useStandardConfiguration()
        .withBaseUrl('https://api.github.com/');
    });

    this.http = http;
  }

  activate() {
    return this.http.fetch('users')
      .then(response => response.json())
      .then(users => this.users = users);
  }
}

fetch polyfillと同じく、HttpClientをAurelia's Fetchプラグインからインポートします。これによって、HTTPリクエストを簡単に行うことが出来ます。デフォルトではこのプラグインは含まれませんが、スターターキットにはすでに含まれています。

injectデコレーターを見てみましょう。ここでは何を行っているのでしょう?Aureliaはアプリを描画するために必要なUIコンポーネントを作成します。Dependency InjectionコンテナがHttpClientのようなコンストラクタ依存を供給することで可能になります。では、どのようにDIは何を供給すべきかを知るのでしょうか?あなたが行うことといえば、ES2016のinjectデコレータをクラスに追加することだけですが、これで、供給するインスタンスの種類のリストを渡します。それぞれのコンストラクタパラメーターにはひとつの引数を用意します。上記の例では、HttpClientインスタンスが必要なので、injectデコレータにHttpClientタイプを追加し、コンストラクタに対応したパラメーターを渡します。

Aureliaのルーターはルーターの変更時にビューモデルのライフサイクルを実施します。"Screen Activation Lifecycle"や"Navigation Lifecycle"と呼ばれるものです。ルートの内外に対する流れをコントロールするために、ビューモデルは任意でライフサイクルの多様な部分にフックすることが出来ます。ルートがアクティベートする準備ができた時には、ルーターはactivateフックを呼び出します。上記のコードでは、Github APIを呼び出して、ユーザーを取得するためにこのフックを使用しています。activateメソッドで、httpリクエストの結果を返していることに注意して下さい。すべてのHttpClient APIはPromiseを返します。ルーターはPromiseを探知し、それがresolveするまでナビゲーションの完了を待機します。 つまり、この場合では、データが入れられるまで、任意でルーターにページの表示を遅らせることができるのです。

users.html

<template>
  <section>
    <h2>${heading}</h2>
    <div class="row au-stagger">
      <div class="col-sm-6 col-md-3 card-container" repeat.for="user of users">
        <div class="card">
          <canvas class="header-bg" width="250" height="70"></canvas>
          <div class="avatar">
            <img src.bind="user.avatar_url" crossorigin />
          </div>
          <div class="content">
            <p class="name">${user.login}</p>
            <p><a target="_blank" class="btn btn-default" href.bind="user.html_url">Contact</a></p>
          </div>
        </div>
      </div>
    </div>
  </section>
</template>

このスクリーンのビューはきわめてシンプルです。初めて見るものはないでしょう。

要約すると次のようになります。アプリにページを追加するには: 1. app.jsルーターにルートを設定します。 2. ビューモデルを作成します。 3. 同じ名前のビューを作成します。 4. お祭り騒ぎします。

Bonus: Creating a Custom Element

延長戦です。カスタムHTMLエレメントを作成しましょう。"navbar"が適した候補だと思います。たくさんのHTMLがapp.htmlファイルにあります。<nav-bar>エレメントを抽出して、少し叙述的にします。最後には次のように記述することが出来ます。

app.html

<template>
  <require from="bootstrap/css/bootstrap.css"></require>
  <require from="font-awesome/css/font-awesome.css"></require>
  <require from="./nav-bar"></require>

  <navbar router.bind="router"></navbar>

  <div class="page-host">
    <router-view></router-view>
  </div>
</template>

このコードは"nav-bar"のnav-barエレメントを必要とします。ひとたびビューにおいて、このエレメントが利用可能になると、(routerのような)カスタムプロパティへのデータバインディングを含め、その他のエレメントと同じように使用することが出来ます。

まずは、nav-bar.jsnav-bar.htmlを作成しましょう。

nav-bar.js

import {bindable} from 'aurelia-framework';

export class NavBar {
  @bindable router = null;
}

カスタムエレメントを作成するために、クラスを作成し、それをエクスポートします。このクラスはHTMLでエレメントとして使用されるので、どんなプロパティがエレメントの要素として表れるべきかをフレームワークに教える必要があります。そのために、bindableデコレータを使用します。injectと似て、bindableはAureliaにクラスについての情報を提供します。bindableデコレータは、フレームワークに対して、routerプロパティをHTMLの要素として表示して欲しいと伝えます。要素として表示されれば、ビューとバインドすることが出来ます。

nav-bar.html

<template>
  <nav class="navbar navbar-default navbar-fixed-top" role="navigation">
    <div class="navbar-header">
      <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
        <span class="sr-only">Toggle Navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
      <a class="navbar-brand" href="#">
        <i class="fa fa-home"></i>
        <span>${router.title}</span>
      </a>
    </div>

    <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
      <ul class="nav navbar-nav">
        <li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
          <a href.bind="row.href">${row.title}</a>
        </li>
      </ul>

      <ul class="nav navbar-nav navbar-right">
        <li class="loader" if.bind="router.isNavigating">
          <i class="fa fa-spinner fa-spin fa-2x"></i>
        </li>
      </ul>
    </div>
  </nav>
</template>

これはもともとのapp.htmlファイルのnavbar HTMLとほとんど同じに見えますね。でも、app.jsにバインディングするのではなく、ここではnav-bar.jsにバインドしています。

このカスタムエレメントはとてもシンプルです...が、シングルrouterプロパティを定義するためにES 2016のクラスを必要とするのは、すこし馬鹿げているように見えます。どうにか取り除けないでしょうか?答えは、YESです。振る舞いのない、しかし、一連のプロパティがバインドされるビューを供給する、とてもシンプルなエレメントでは、省略することが出来るのです。

まずは、nav-bar.jsファイルを削除します。次に、nav-bar.htmlファイルをひとつ変更します。templateエレメントでは、次のようにbindableプロパティを宣言することができます。

nav-bar.html

<template bindable="router">
  ...
</template>

最後に、requireエレメントhtmlコンポーネントを指すので、app.htmlファイルを更新する必要があります。

app.html

<template>
  <require from="bootstrap/css/bootstrap.css"></require>
  <require from="font-awesome/css/font-awesome.css"></require>
  <require from='./nav-bar.html'></require>

  <nav-bar router.bind="router"></nav-bar>

  <div class="page-host">
    <router-view></router-view>
  </div>
</template>

Aureliaがどのようにカスタムエレメントの名前を決めているのか不思議に思うでしょう。慣例では、エクスポートされた、小文字かつハイフンで繋がれたクラス名を使用します。HTMLのみの場合では、ファイル名を使用します。

カスタムエレメントの作成に加えて、既存のエレメントに新しい振る舞いを追加するカスタムアトリビュートを作成することも出来ます。repeatifのように、ビューからDOMを追加したり削除することで、直接テンプレートをコントロールするアトリビュートが必要になることがあるでしょう。Aureliaのパワフルな、かつ拡張可能なテンプレートエンジンでは、これが可能になります。これは秘密ですが...いわゆるAureliaの"ビルトイン"の振る舞いには、実際にビルトインされているものはありません。それらは独自のライブラリに存在し、プラグインとしてAureliaに"インストール"されるのです。私たちは、あなたが自身のアプリとプラグインにビルドしなければならないものと同じコアを使用する"ビルトイン"を提供します。

Bonus: Leveraging Child Routers

独自のルータを持つ三番目のページを追加しましょう。これを子ルーターと呼びます。子ルーターはそれぞれ自身のルーター設定を持ち、親ルーターへナビゲートします。

まずは、app.jsを更新しましょう。

app.js

export class App {
  configureRouter(config, router) {
    config.title = 'Aurelia';
    config.map([
      { route: ['','welcome'],  name: 'welcome',      moduleId: './welcome',      nav: true, title:'Welcome' },
      { route: 'users',         name: 'users',        moduleId: './users',        nav: true, title:'Github Users' },
      { route: 'child-router',  name: 'child-router', moduleId: './child-router', nav: true, title:'Child Router' }
    ]);

    this.router = router;
  }
}

何も新しい物はありません。面白いのはchild-router.jsです。

child-router.js

export class ChildRouter {
  heading = 'Child Router';

  configureRouter(config, router){
    config.map([
      { route: ['','welcome'],  name: 'welcome',       moduleId: './welcome',       nav: true, title:'Welcome' },
      { route: 'users',         name: 'users',         moduleId: './users',         nav: true, title:'Github Users' },
      { route: 'child-router',  name: 'child-router',  moduleId: './child-router',  nav: true, title:'Child Router' }
    ]);

    this.router = router;
  }
}

これ、部分的にはAppの設定と同じですよね。これが、再帰的ルーターです。

ビューを作成して、完成させましょう。

child-router.html

<template>
  <section>
    <h2>${heading}</h2>
    <div>
      <div class="col-md-2">
        <ul class="well nav nav-pills nav-stacked">
          <li repeat.for="row of router.navigation" class="${row.isActive ? 'active' : ''}">
            <a href.bind="row.href">${row.title}</a>
          </li>
        </ul>
      </div>
      <div class="col-md-10" style="padding: 0">
        <router-view></router-view>
      </div>
    </div>
  </section>
</template>

アプリを実行して、魔法を目の当たりにしなさい...そして宇宙が爆発しないように祈りなさい。

Conclusion

Aureliaは素晴らしいアプリケーションを作成出来るだけでなく、そのプロセスを楽しむことも出来ます。私たちはシンプルな慣習によってデザインしました。なので、デベロッパーは、たくさんの設定や、頑固で、制限の多いフレームワークを満たすためのボイラープレートコードを記述に、時間をムダにすることがなくなります。Aureliaを使えば、もう障害物にぶつかることがなくなるでしょう。Aureliaはプラグシステムを使用し、カスタムできるように注意深くデザインされています。

Atomエディタから、はてなブログのエントリーを編集できるようになりました

以前から開発していた、Atomからはてなブログにエントリーを投稿できるようにするパッケージ

hatena-blog-entry-post

にプルリクエストをもらって、エントリーが編集できるようになりました。

ついでに日本語ドキュメントも作成しました。

パッケージはapm install hatena-blog-entry-postでインストールできます。

Processingでカレンダーを作る

カレンダーを作ってみたが、長いし読み解きにくいコードになってしまった。input areaみたいなのは作れないのかな?

processing

int defaultYear = 2015;
int defaultMonth = 5;
int rectRX, rectRY;
int rectLX, rectLY;
int rectSize = 20;
boolean leftButtonOver = false;
boolean rightButtonOver = false;
color currentColor = 200;

void setup() {
  size(800, 700);
  background(currentColor);
  rectLX = width/2-50;
  rectLY = 50;
  rectRX = width/2+50;
  rectRY = 50;
}

void draw() {
  update();
  drawCalendar(defaultYear, defaultMonth);

  if (rightButtonOver) {
    fill(255);
  } else {
    fill(0);
  }
  rect(rectRX, rectRY, rectSize, rectSize);

  if (leftButtonOver) {
    fill(255);
  } else {
    fill(0);
  }
  rect(rectLX, rectLY, rectSize, rectSize);
}

void update() {
  if (overLeftButton(rectLX, rectLY, rectSize, rectSize)) {
    leftButtonOver = true;
    rightButtonOver = false;
  } else if (overRightButton(rectRX, rectRY, rectSize, rectSize)) {
    leftButtonOver = false;
    rightButtonOver = true;
  } else {
    leftButtonOver = false;
    rightButtonOver = false;
  }
}

void mousePressed() {
  if (leftButtonOver) {
    background(currentColor);

    if (defaultMonth == 1) {
      defaultMonth = 12;
      defaultYear -= 1;
    } else {
      defaultMonth -= 1;
    }
  }
  if (rightButtonOver) {
    background(currentColor);

    if (defaultMonth == 12) {
      defaultMonth = 1;
      defaultYear += 1;
    } else {
      defaultMonth += 1;
    }
  }
}

boolean overRightButton(int x, int y, int width, int height) {
  if (mouseX >= x && mouseX <= x+width && mouseY >= y && mouseY <= y+height) {
    return true;
  } else {
    return false;
  }
}

boolean overLeftButton(int x, int y, int width, int height) {
  if (mouseX >= x && mouseX <= x+width && mouseY >= y && mouseY <= y+height) {
    return true;
  } else {
    return false;
  }
}

void drawCalendar(int year, int month) {
  fill(0);
  text(year, width/2-20, 30);
  text(month, width/2+20, 30);

  int days = daysOfMonth(year, month);

  // draw days of the Month
  for (int day = 1; day <= days; day++) {
    fill(50);

    int x = dayOfWeek(year, month, day);
    int y = weekOfMonth(year, month, day);

    fill(0);
    noFill();
    rect(x*100+40, y*100-20, 100, 100);

    color z = setColor(x);
    fill(z);
    text(day, x*100+50, y*100);
  }
}

color setColor(int day) {
  if (day == 0) {
    return #FF0000;
  } else if (day == 6) {
    return #0008FF;
  } else {
    return #000000;
  }
}

int weekOfMonth(int year, int month, int date) {
  return (date - dayOfWeek(year, month, date) + 12) / 7;
}

int daysSum(int year, int month, int date) {
  // 2000,1,1
  int daysSum = 1;
  for (int y = year-1; y >= 2000; y--) {
    if (isLeapYear(y)) {
      daysSum += 366;
    } else {
      daysSum += 365;
    }
  }

  for (int m = month-1; m >= 1; m--) {
    daysSum += daysOfMonth(year, m);
  }

  daysSum += date;
  return daysSum;
}

int dayOfWeek(int year, int month, int date) {
  int daysSum = daysSum(year, month, date);
  // 0 - Sun / 6 - Sat
  return daysSum % 7;
}

boolean isLeapYear(int year) {
  if (year % 4 == 0 && year % 100 != 0) {
    return true;
  } else {
    return false;
  }
}

// 4,6,9,11 has 30 days.
int daysOfMonth(int year, int month) {
  if (month == 4 || month == 6 || month == 9 || month == 11) {
    return 30;
  } else if (month == 2) {
    if (isLeapYear(year)) {
      return 29;
    } else {
      return 28;
    }
  } else {
    return 31;
  }
}

最近読んだ論文まとめ(随時更新)

最近読んだ論文の要約。Google Scholarで論文検索やGoogle検索すれば読めるものも。

カレンダー

機械学習関連

Web上での人物評価関連

  • Kollock, Peter. "The production of trust in online markets." Advances in group processes 16.1 (1999): 99-123.

    • オンラインオークション等、個人の人物評価情報が必要とされるサービスにおいては、レーティングシステムが使われる。レーティングシステムを効果的にするは、レーティングするためのコストが低いこと、レーティングを比較できる取引相手が多くいること、他人になりすますことが難しいこと、レーティングシステムのホストが信頼できること、が挙げられる。
  • Das, Manoj. "Using Social Networking Sites (SNS): Mediating Role of Self Disclosure and Effect on Well-being." Editorial Team: 30.

    • ヒトは自己開示(他人と自分のことについて共有すること)で、結束を強くする。例えば友だちがいなくとも、社会から疎外されても、自分の容姿に自信がなくとも、テクストベースのSNSでは、これらはフラットなので、気にせずSNSを使うことができる。SNS上で自己開示することで、社会的結束を感じ、幸福になる。
  • 緒方進, et al. "Web 上のテキスト情報を用いた人物評価手法." 情報処理学会研究報告 (2005): 9-14.

    • Web上の書き込み記事から人物の特徴を評価するために、テキストマイニング手法を適用し、EQ(Emotional Intelligence Quotient)で人物評価を行った。

通知システム関連

  • Ho, Joyce, and Stephen S. Intille. "Using context-aware computing to reduce the perceived burden of interruptions from mobile devices." Proceedings of the SIGCHI conference on Human factors in computing systems. ACM, 2005. APA

    • モバイルデバイスからのメッセージ等情報提示の過度な負担を軽減するために、加速度計付きのコンテキスト・アウェア・コンピューティング・デバイスを開発して計測した結果、ランダムに情報提示するより、2つの身体動作の移行期に情報提示した時のほうが、負担が軽減されることがわかった。
  • 笠井裕之, and 倉掛正治. "受信ユーザ状況に依存したモバイル向け情報通知制御システム." 情報処理学会論文誌 48.3 (2007): 1393-1404.

    • ユーザーがモバイル端末画面上に注意を向けているかどうか、モバイル端末上のタスクの状況に着目。通知の提示時間やタイミングを制御することで、ユーザーのタスクを極力邪魔することなく、ユーザーが気づきやすいように情報を提示できる機能、さらにユーザーが気付かなかったであろう情報を特定し再掲示する機能を提供する
  • 三好史隆, et al. "タスク集中度と認知時間を指標とした周辺表示法の評価." 電子情報通信学会論文誌 A 89.10 (2006): 831-839.

    • 情報が提示された時に周辺表示法がユーザーに与える”情報提示の気づかせやすさ”と”メインタスクへの妨害の度合い”を定量化することで、あらかじめ情報の重要度がわかっている場合、周辺情報の重要度や緊急性を考慮して提示方法を選択することができる。
  • 次読む論文

20分で作るFacebookメッセンジャーボット(hubot)

20分で作るFacebookメッセンジャーボット(hubot)

すごーく簡単にFacebookメッセンジャーボットが作成できました。後のためにその手順を分かりやすくメモしておきます。

別にhubotでなくともボットは作成できますが、今回は使い方に慣れているhubotを使用します。

hubot

hubotの作成

hubot作成手順については以前の記事参考。

uraway.hatenablog.com

アダプターのインストール

hubot作成後、Facebookメッセンジャーアダプタであるhubot-fbをインストールします。

$ npm install -S hubot-fb

次にProcfileを編集します。

web: bin/hubot -a fb

Facebookページ、アプリの作成、設定

メッセンジャーボットを利用するには、FacebookページとFacebookアプリが必要になります。

Facebookページの作成

https://www.facebook.com/pages/create/

上記のページから、ジャンルやその他必要事項を入力し、Facebookページを作成します。

Facebookアプリの作成

https://developers.facebook.com/quickstarts/?platform=web

Facebookディベロッパー登録後、上記のページにアクセスします。

画像のボタンをクリックして、アプリのディスプレイネームやメールアドレス等必要な情報を入力して、新しくアプリを作成します。

トークンの設定

アプリIDの新規作成に成功すると、アプリのダッシュボードに移動します。

サイドバーの"Messenger"をクリックして、ウィザードを起動、Messagerプラットフォームを有効にしましょう。

次に、"Token Generation"から作成したFacebookページを選択し、アクセストークンを生成します。生成されたトークンをコピーしておきましょう。

アプリの登録

先ほど取得したトークンを使い、次のcurlコマンドを実行しましょう。

curl -ik -X POST "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=[FB_PAGE_TOKEN]"

Herokuで動かす

hubotのスクリプトをherokuに上げましょう。

Herokuにプッシュ

まずはgitにコミット。

$ git init
$ git add .
$ git commit -m "first commit"

次にherokuにプッシュ。その際、わかりやすいアプリの名前をつけておきます。

$ heroku apps:create アプリ名
$ git push heroku master

heroku側の設定として、環境変数を使う必要があります。FB_PAGE_TOKENは先ほど生成したもの。FB_VERIFY_TOKENは適当な文字列を使用します。

$ heroku config:set FB_PAGE_TOKEN=****
$ heroku config:set FB_VERIFY_TOKEN=abcd

Webhooks

Webhookを設定することで、ユーザーからのメッセージ等を受け取ることが可能になります。

アプリのダッシュボードからWebhookの設定を行いましょう。

Setup Webhooksをクリックすると、次のようなダイアログが表示されます。

Callback URLにはhubotのURLに/hubot/を追加したものを入力しましょう。

例:

アプリ名.herokuapp.com/hubot/

"Verify Token"の欄には、先ほどherokuに設定したFB_VERIFY_TOKENを入力します。

"Subscription Fields"のすべてのチェックボックスをチェックし、"Verify and Save"をクリックして完了です。

実際にメッセージを送る

適当にスクリプトを追加して、Facebookページを検索して、語りかけてみましょう。

module.exports = (robot) ->
  robot.hear /疲れた/i, (res) ->
    res.send "頑張って!"

人工知能/機械学習: ベイジアンフィルタによるテクスト分類

人工知能/機械学習: ベイジアンフィルタによるテクスト分類

機械学習には多種ありますが、今回は自然言語処理、その中でも特に、ベイジアンフィルタについて扱ってみたいと思います。

このエントリを書くにあたって、下記の書籍がかなり参考になりました。機械学習のみならず、Webデータ全般について、取得法から解析法、利用法までを初心者にも分かりやすく解説しています。

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック

ベイジアンフィルタとは、「ナイーブベイズ分類」を応用したもので、対象のデータを解析・学習するためのフィルタです。

フィルタにはキーワード指定のブラックリスト・ホワイトリスト方式から、ベイジアンフィルタを代表とする学習タイプのフィルタまで、幅広く存在します。

テクスト分類とは

テクスト分類とは、与えられた対象のテクストが、同じく与えられたクラスのどれに属するかを判断するタスクを指します。こうしたクラスは、たいてい一般的な主題になります。

この技術は、身近なところでは、

  • Eメールのソート: 受信したメールを「家族から」「SNSから」「広告」といった正しいフォルダ(クラス)に分類する。
  • レビューのクラス分け: 製品や映画のレビューがポジティブであるか、ネガティブであるかを判断し、自動的に分類する。

などに使われています。

テクスト分類には、手作業で行われるものも多々あります。例えば、映画のジャンル分けには、明確な基準はなく、広告代理店の売り方次第で変わり得るものです(多分)。こうした手動の分類は、大規模になると大変です。そこで、機械学習をもとにした自動テクスト分類が必要とされます。

アルゴリズム

ベイジアンフィルタ、つまり、ナイーブベイズ分類のアルゴリズムについて見てみましょう。

文書dが与えられたときに、カテゴリcである確率を、$P(c|d)$と表します。そしてこれを、ベイズの定理で表すと、次のようになります。

$$ P(c|d)=\frac {P(c) P(d|c)}{P(d)} $$

一つ一つ見ていきましょう。$P(c)$とは与えられたある文書がそのカテゴリである事前確率です。つまり、メール1000通中50通がスパムであれば、

P(スパム) = 50 / 1000 = 0.05

となります。

$P(c|d)$とは、ここで求めたい結果、つまり、ある文書dが与えられたとき、カテゴリcである事後確率です。次に、$P(d|c)$とはカテゴリcに文書dが生成される確率で、統計学では尤度(ゆうど)と呼ばれます。$P(d)$はすべての文書中、文書dが生成される確率です。

ここで、文書dは単語の出現場所を考慮しない単語の集合$(t_1, t_2, ... , t_i)$とします。すると次の式が成り立ちます。

$$ P(d|c)=P(t_1|c)\ P(t_2|c)\ ...\ P(t_i|c)=\prod_i P(t_i|c) $$

第三式のギリシア文字は積の記号らしいです。さっき知りました

今度は、$P(t_i|c)$を求めましょう。あるカテゴリにその単語が出現する確率です。これは訓練データのカテゴリcに単語$t_i$が出てきた回数をカテゴリcの全単語数で割ることで計算できます。

テクスト分類においては、ある文書のもっともそれらしいカテゴリを求めることが目標です。ナイーブベイズ分類では、それはカテゴリcである最大事後確率(maximum a posteriori)の、$c_{map}$で表します。

$$ c_{map}=arg\ max_c P(c|d)=arg\ max_c P(c) \prod_i P(t_i|c) $$

もし、訓練データにおいて、WTOという単語がChinaカテゴリの文書にのみ出現するとき、UKカテゴリにWTOが出現する確率$P(WTO|UK)$について、次の式が成り立ちます。

$$ P(WTO|UK)=0 $$

次に、対象の文書Britain is a member of the WTOが与えられたとします。この文書はBritainを含むので、UKカテゴリである確率が高そうです。しかし、訓練データではUKカテゴリに出てこなかったWTOという新単語が含まれているために、この文書をd1とした時、$P(d1|UK)=0$となり、この文書がカテゴリUKから生成された確率はゼロとなってしまいます。

というのも、カテゴリcに文書dが生成される尤度$P(d|c)$は、単語が出現する確率$P(t|c)$の積で求められるためです。

これを解消するために、各単語を出現回数にシンプルに1を足す、add-one, Laplace-smoothing が使用されます。

ベイジアンフィルタのNodeライブラリ

さて、アルゴリズムについて理解したところで、いや、実は理解していなくともいいんですが、実際にベイジアンフィルタを使用してみましょう。

Node.js には、ベイジアンフィルタを実装したbayesライブラリが存在します。以下、このライブラリを使用して、ベイジアンフィルタを実装します。

まずはインストール。

$ npm install bayes mecab-lite

次にjsファイルを編集します。今回はサンプルとして、Wikipediaから「織田信長」、「明智光秀」、「豊臣秀吉」について学習するフィルタを実装することにしましょう。

var bayes = require('bayes');
var Mecab = require('mecab-lite');
var mecab = new Mecab()

var nobunaga = '織田 信長(おだ のぶなが)は、戦国時代から安土桃山時代にかけての武将・戦国大名。三英傑の一人。尾張国(現在の愛知県)の古渡城主・織田信秀の嫡男[注釈 5]。 尾張守護代の織田氏の中でも庶流・弾正忠家の生まれであったが、父の代から主家の清洲織田氏(織田大和守家)や尾張守護の斯波氏をも凌ぐ力をつけ、家督争いの混乱を収めて尾張を統一し、桶狭間の戦いで今川義元を討ち取ると、婚姻による同盟策などを駆使しながら領土を拡大した。足利義昭を奉じて上洛すると、将軍、次いでは天皇の権威を利用して天下に号令。後には義昭を追放して室町幕府を事実上滅ぼし、畿内を中心に強力な中央集権的政権(織田政権)を確立して天下人となった。これによって他の有力な大名を抑えて戦国乱世の終焉に道筋をつけた。しかし天正10年6月2日(1582年6月21日)、重臣・明智光秀に謀反を起こされ、本能寺で自害した。すでに家督を譲っていた嫡男・織田信忠も同日に二条城で没し、信長の政権は、豊臣秀吉による豊臣政権、徳川家康が開いた江戸幕府へと引き継がれていくことになる。';

var mitsuhide = '明智 光秀(あけち みつひで)は、戦国時代から安土桃山時代にかけての武将。戦国大名・織田信長に見出されて重臣に取り立てられるが、本能寺の変を起こして信長を暗殺。直後に中国大返しにより戻った羽柴秀吉に山崎の戦いで敗れた。一説では、落ちていく途中、小栗栖において落ち武者狩りで殺害されたとも[注釈 4][6]致命傷を受けて自害したもとされる[7]。これは光秀が信長を討って天下人になってからわずか13日後のことであり、その短い治世は「三日天下」とも言う。本姓は源氏で、家系は清和源氏の摂津源氏系で、美濃源氏土岐氏支流である明智氏。通称は十兵衛。雅号は咲庵(しょうあん)。のちに朝廷より惟任の姓を賜ったため惟任光秀とも言う。妻は妻木煕子。その間には、細川忠興室・珠(洗礼名:ガラシャ)、嫡男・光慶(十五郎)、津田信澄室がいる。領地では善政を行ったとされ、忌日に祭事を伝える地域(光秀公正辰祭・御霊神社 (福知山市))もある。後世、江戸時代の文楽「絵本太功記」や歌舞伎「時桔梗出世請状」をはじめ、小説・映画・テレビドラマなどでもその人物がとりあげられている。';

var hideyoshi = '豊臣秀吉(とよとみ ひでよし、とよとみ の ひでよし、旧字体: 豐臣秀吉)、または羽柴 秀吉(はしば ひでよし)は、戦国時代から安土桃山時代にかけての武将、大名、天下人、関白、太政大臣、太閤。三英傑の一人[1][2]。初め木下氏を名字とし、羽柴氏に改める。本姓としては、初め平氏を自称するが、近衛家の猶子となり藤原氏に改姓した後、豊臣氏に改めた。尾張国愛知郡中村郷の下層民の家に生まれたとされる。(出自参照)当初、今川家に仕えるも出奔した後に織田信長に仕官し、次第に頭角を現した。信長が本能寺の変で明智光秀に討たれると「中国大返し」により京へと戻り山崎の戦いで光秀を破った後、信長の孫・三法師を擁して織田家内部の勢力争いに勝ち、信長の後継の地位を得た。大坂城を築き、関白・太政大臣に就任し、豊臣姓を賜り、日本全国の大名を臣従させて天下統一を果たした。天下統一後は太閤検地や刀狩令、惣無事令、石高制などの全国に及ぶ多くの政策で国内の統合を進めた。理由は諸説あるが明の征服を決意して朝鮮に出兵した文禄・慶長の役の最中に、嗣子の秀頼を徳川家康ら五大老に託して病没した。秀吉の死後に台頭した徳川家康が関ヶ原の戦いで勝利して天下を掌握し、豊臣家は凋落。慶長19年(1614年)から同20年(1615年)の大坂の陣で豊臣家は江戸幕府に滅ぼされた。墨俣の一夜城、金ヶ崎の退き口、高松城の水攻め、中国大返し、石垣山一夜城などが、機知に富んだ功名立志伝として広く親しまれ、百姓から天下人へと至った生涯は「戦国一の出世頭」と評される。';

var classifier = bayes({
  tokenizer: function(text) {
    return mecab.wakatigakiSync(text);
  }
});

// 学習
classifier.learn(nobunaga, '織田信長');
classifier.learn(mitsuhide, '明智光秀');
classifier.learn(hideyoshi, '豊臣秀吉');

// 与えられたテキストデータのカテゴリを予想
categorize('部下に謀反を起こされる');
categorize('三日天下');
categorize('下層農民の生まれ');

function categorize(text) {
  var r = classifier.categorize(text);
  console.log("[" + r + "] - " + text);
}

以下出力結果です。ちゃんと分類されていますね。

[織田信長] - 部下に謀反を起こされる
[明智光秀] - 三日天下
[豊臣秀吉] - 下層農民の生まれ

英語と違って、日本語の文章は単語間の区切りがコンピューターにとってはわかりにくいので、分かち書きしたものをベイジアンフィルタにトークンとして与えています。

参考文献

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック

JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック

情報検索の基礎

情報検索の基礎

  • 作者: Christopher D.Manning,Prabhakar Raghavan,Hinrich Schutze,岩野和生,黒川利明,濱田誠司,村上明子
  • 出版社/メーカー: 共立出版
  • 発売日: 2012/06/23
  • メディア: 単行本
  • 購入: 2人 クリック: 69回
  • この商品を含むブログ (5件) を見る