Wednesday, July 10, 2013

Baasdayを使ったチャットアプリ制作

はじめまして。
とある事情で群馬からニャンパスへ出向中の白石です。
今回、iOSのチャットアプリを作ることになりました。
とは言うものの、普段はほとんどフロントエンドのプログラムしか書かないのでサーバーを必要とするチャットアプリを作れと言われても、ぱっぱとこなせません。
そこでBaasdayを使いました。

Baasdayとは

ニャンパスが開発を行っているBaas(Backend as a Service)です。
Baasとはサーバーを立てて、サーバーサイドのプログラミングのコードを書かなくても
アプリからBaasサービスのAPIを呼ぶことでユーザー管理やデータ管理などが行えるサービスです。

コレクションを決める

まずチャットアプリに使うコレクションを
  • ユーザー
  • チャットルーム
  • メッセージ
としました。Baasdayでは動的にコレクションやオブジェクトが作成できるので、SQLのように事前にテーブルを作成してカラムを決めておく必要がなくて、ちょっとオブジェクトにキーを追加したいと思ったときでも柔軟に対応できました。

Baasdayの認証

ここから実際にBaasdayを使用していきます。
Baas dayを使うにあたって最初にしなければいけないのアプリケーション認証です。
[Bastardy setApplicationId:applicationId apiKey:apiKey];
applicationIdとapiKeyには管理画面にあるアプリケーションのキー情報を使用します。

ユーザーの作成

ユーザーに関してBaasdayではすでに専用のコレクションが存在しています。
BDAuthenticatedUser *user = [BDAuthenticatedUser createWithValues: @{@”name” : name}];
認証済みユーザーを作成します。ユーザー情報は、アプリケーション認証では更新できず、 ユーザー認証からしか更新できません。2回目以降アプリを起動する際にこの端末のユーザーを取得するために
NSString *key = [user authenticationKey];
で取得した認証キーをKeychainに保存しました。自分はBaasdayを使い始めたころ、認証キーを保存することを知らなかったので、アプリを起動するたびにユーザーが生成されて、かなり使わないユーザーが増えてしまいました・・・。
保存した認証キーからこの端末のユーザーを取得するには
[BDBaasday setUserAuthenticationKey:key];
BDAuthenticatedUser *user = [BDAuthenticatedUser fetch];
で取得できます。

チャットルームの作成

ユーザーの作成が出来たら次はチャットルームを建てます。チャットルームに必要なは入室したユーザーとそのユーザーがまだ入室中かどうかです。自分をチャットルームのuser1としてroomsのオブジェクトを作成します。
BDItem *room = [BDItem createWithCollectionName:@"rooms" values:@{@”user1” : user.id, @”user2” : @””, @”is_user1” : @YES, @”is_user2” : @YES}];
これでチャットルームが作成されました。また、すでに作成されたチャットルームを探すのには
BDQuery *query = [[BDQuery alloc] init];"
query.filter = @{@”user2” : @””};
 BDListResult *rooms = [BDItem fetchAllWithCollectionName:@"rooms" query:query];
if(rooms & rooms.count == 0){
}
で空いているチャットルームを検索できます。チャットルームが見つかったら、すでに使用中であるチャットルームであることを知らせるためにチャットルームを更新します。
[room update:@{@”user2” : user.id, @”is_user2” : @YES}];
ユーザーが退室するときにも、ユーザーが退室したことを知らせるためにroomを更新します。
[room update:@{@”is_user2” : @NO}];
Baasday上のデータを更新しただけではユーザーが退室したことを相手のユーザーは知ることができないので、NSTimerクラスを使用して定期的に確認するようにします。
NSTimer *tm =
     [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(checkLeft:) userInfo:nil repeats:YES];
     [tm fire];
- (void) checkLeft:(NSTimer *)timer{
    room = [BDItem fetchWithCollectionName:@"rooms" id:room.id];
    BOOL is_user = [[room objectforkey@”is_user2”];
    if(is_user){
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"お知らせ" message:[NSString stringWithFormat:@"%@さんが退室しました。",[user2 objectFor@”name”] delegate:self cancelButtonTitle:@"確認" otherButtonTitles:nil, nil];
        [alert show];
    }
}

メッセージの作成

チャットルームにユーザーが入室したので、残りはメッセージです。メッセージにはどの部屋のメッセージなのか、誰のメッセージなのかわかるように
[BDItem createWithCollectionName:@"messages" values:@{@"body" : @"text", @"room_id" : room.id, @"user_id" : user.id}];
としました。このメッセージを受け取るのにもNSTimerクラスを使うので、先ほどのcheckLeftメソッドの中に
BDQuery *query = [[BDQuery alloc] init];
//メッセージのソートを作成日時の降順にする
query.order = @[[[BDFieldOrder alloc] initWithField:@"_createdAt" descending:YES]];
//lastMessageが存在している場合はlastMessageの作成日時以降のメッセージを取得
//存在していない場合はすべて取得
if (lastMessage) query.filter = @{@"_createdAt": @{@"$gt": lastMessage.createdAt}, @"room_id" : [room objectForKey:@"_id"]};
else query.filter = @{ @"room_id" : [room objectForKey:@"_id"]};

*result = [BDItem fetchAllWithCollectionName:@"messages" query:query];
receivedMessages = result ? result.contents : nil;

if (receivedMessages & receivedMessages.count == 0) {
    for (id message in receivedMessages.reverseObjectEnumerator) {
        [messages insertObject:message atIndex:0];
    }
    //UITableViewのCellを更新する
    [chatTable reloadData];
    lastMessage = receivedMessages[0];
}
を追加します。

問題点

Baasdayではユーザーが回線落ちしたかどうか判別できないので、その場合部屋にいる別の人に回線落ちして部屋から抜けたことを知らせることができません。なので企画の段階で部屋の人数を制限しないでワイワイガヤガヤするチャットアプリにしていたら、1人くらいいつの間にかいなくなっていても気にならないと思うので良かったかもしれません・・・。

最後に

Baasdayを使ってフロントエンドのみの実装でチャットアプリを作成できました。Baasdayは決められた範囲の機能しかサービスできません。それでも、Baasdayは何ができて、何ができないからこういうものを作ろうと企画すれば、バックエンドに割く時間とコストを押さえられるので、Baasdayを活用する価値はあると思います。

Tuesday, November 27, 2012

JavaScriptで音を作る

こんにちは。清水です。

この前、個人的な趣味で「TanTone」というWebブラウザ上で効果音を作れるサイトを作りました。波の形や周波数を決めたり、複数の波を組み合わせたりして、こんな音こんな音こんな音を作ることができます。詳しい使い方はヘルプにて。

この記事ではTanToneの技術的な話として、JavaScriptで音(WAVEファイル)を作る方法を紹介します。

ちなみに、この方法だとGoogle ChromeとSafariでしか動きませんでした。

大まかに書くと
  • WAVEファイルのバイト列を作る
  • BASE64でエンコード
  • audioタグにdata URLスキームとして設定
ということをしています。

以下はそれを簡単に実装したサンプルです。
<html>
  <head></head>
  <body>
    <input type="button" value="Play" id="play-button">
    <audio id="audio" autoplay="autoplay"></audio>
    <script type="text/javascript">
      var base64Encode = function(bytes) {
          var base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
          var bytesLength = bytes.length;
          var encoded = '';
          var paddingLength = 3 - bytesLength % 3;
          if (paddingLength == 3) paddingLength = 0;
          var rest = 0;
          for (var i = 0; i < bytesLength; ++i) {
              var shiftLength = (i % 3) * 2 + 2;
              encoded += base64Characters.charAt((rest << (8 - shiftLength)) | (bytes.charCodeAt(i) >> shiftLength));
              rest = bytes.charCodeAt(i) & (0xff >> (8 - shiftLength));
              if (i % 3 == 2) {
                  encoded += base64Characters.charAt(rest);
                  rest = 0;
              }
          }
          if (bytesLength % 3 != 0) encoded += base64Characters.charAt(rest << (paddingLength % 3 * 2));
          for (var i = 0; i < paddingLength; ++i) encoded += '=';
          return encoded;
      };
      var bytesFromInt = function(value, bytes) {
          if (value == undefined || bytes == undefined || isNaN(value) || value == Infinity) throw 'Invalid value: ' + value + ' or bytes: ' + bytes;
          var intValue = Math.floor(value);
          var result = '';
          for (var i = 0; i < bytes; ++i) {
              var byte = (intValue & (0xFF << (i * 8))) >> (i * 8);
              result += String.fromCharCode(byte);
          }
          return result;
      };
      window.onload = function() {
          document.getElementById('play-button').onclick = function() {
              var samplesPerSecond = 44100;
              var channels = 2;
              var bytesPerSample = 2;
              var bytes = '';
              var formatPartLength = 16;
              var dataLength = samplesPerSecond * channels * bytesPerSample;
              bytes += 'RIFF';
              bytes += bytesFromInt(4 + 4 + 4 + formatPartLength + 4 + dataLength, 4);
              bytes += 'WAVE';
              bytes += 'fmt ';
              bytes += bytesFromInt(formatPartLength, 4);
              bytes += bytesFromInt(1, 2);
              bytes += bytesFromInt(channels, 2);
              bytes += bytesFromInt(samplesPerSecond, 4);
              bytes += bytesFromInt(samplesPerSecond * bytesPerSample * 8 * channels, 4);
              bytes += bytesFromInt(bytesPerSample * 8 * channels, 2);
              bytes += bytesFromInt(bytesPerSample * 8, 2);
              bytes += 'data';
              bytes += bytesFromInt(dataLength, 4);
              var waveFraction = 440;
              for (var sample = 0; sample < samplesPerSecond; ++sample) {
                  var value = Math.sin(Math.PI * 2 * sample / samplesPerSecond * waveFraction) * 32767 * 0.5;
                  for (var channel = 0; channel < channels; ++channel) {
                      bytes += bytesFromInt(value, 2);
                  }
              }
              document.getElementById('audio').src = 'data:audio/wav;base64,' + base64Encode(bytes);
          };
      };
    </script>
  </body>
</html>

HTMLとしてまるっと保存すると使えるハズです。"Play"というボタンを押すと「ラ」の音(440Hzの正弦波)が1秒間鳴ります。

バイト列を作る

バイト列を作りaudioタグに設定するまでの処理は
document.getElementById('play-button').onclick
の中で行っています。この中の
bytes += 'RIFF';
の行からバイト列を作り始めています。バイト列は文字列として作成し、数値データは真ん中くらいにある bytesFromInt 関数でバイト列に変換しています。

WAVEファイルのフォーマットについては近藤正芳さんのページ「wav ファイルフォーマット」を参考にしました。とてもわかりやすいです。

今回作るデータはサンプリングレート44.1kHz、ステレオ、16ビットで440Hzの正弦波を1秒間含むものです。

bytes += 'data';
辺りまではfmtチャンクの作成です。実際に波形データを作っているのはこの下の
var waveFraction = 440;
for (var sample = 0; sample < samplesPerSecond; ++sample) {
    var value = Math.sin(Math.PI * 2 * sample / samplesPerSecond * waveFraction) * 32767 * 0.5;
    for (var channel = 0; channel < channels; ++channel) {
        bytes += bytesFromInt(value, 2);
    }
}
のループです。1秒間のデータを作るのでsamplesPerSecond(サンプリングレート = 44100)回ループし、波形の情報をchannels(ステレオなので2)個ずつ追加していっています。

このサンプルで作っているのは正弦波と呼ばれる単純な波です。これを作るには時間に対してsinを適用するだけです。それをやってるのが
var value = Math.sin(Math.PI * 2 * sample / samplesPerSecond * waveFraction) * 32767 * 0.5;
このコードです。
Math.sin(Math.PI * 2 * sample / samplesPerSecond * waveFraction)
で今の時間におけるsinの値を計算します。

16ビットPCMの値は-32768から32767になり、0が無音です。なので算出したsinの値(-1から1)に32767をかけて16ビットの値に変換します。それだけだと音量が大きいので今回は0.5をかけて半分にしています。

これを左右の2つずつ44100回追加すれば完成です。

audioタグにセット

作成したバイト列をBASE64エンコードして、先頭に "data:audio/wav;base64," を追加したものをaudioタグのsrcにセットすると、出来上がった音をaudioタグで再生することができます。audioタグにautoplayを設定しておくとsrcをセットした時にすぐ音を鳴らすことができます。


かなり妙な方法で実装しましたが、ちゃんとAudio API的なのを使うともっと簡単にできるのかも知れません。。。

Friday, June 29, 2012

Wink Toolkit入門 ~エフェクト編~

こんにちは。
ニャンパス一味の土井(@exfreeter)です。

ちょっと間が空きましたが、僕は元気です。
前回の続きで、Wink ToolkitのウリであるエフェクトUIを紹介・解説していきます。

が、Wink Toolkitが提供しているUIは多岐にわたるため、その全てを1つのブログ記事で紹介することは到底出来ません。
今回は、特にスマホ的なUIである「Facebookライクな横スライドメニュー」と「引っ張って更新」の2つに絞って紹介することにします。

これらを実装することで一気にスマホらしいUIになりますよ。


Facebookライクな横スライドメニュー

FacebookのiOSネイティブアプリで導入されてから一気に広まった印象があります。
Facebook、Path、Google+、Yammerといった米国の有名スタートアップのアプリが採用していますし、日本のアプリでも増えてきているようです。

デモはこちら(Wink Toolkitのkitchensink。左上のボタンをタッチしてみて下さい。スマホのブラウザ推奨)



使い方等はKitchensinkのソースを見てもらうのが一番早いです。
左上のボタンを押した時に呼ばれるks.options.toggleメソッドが肝ですね。
Chromeの開発ツールか何かで見てもらうと、何をしてるかはすぐ分かると思います。

あの短いコードの中でアニメーションの仕方まで指定していて(ease-in-outの部分)、Winktoolkitの底力を感じられるのではないでしょうか。

引っ張って更新(Scroll To Refresh)

iOSのネイティブアプリでは非常にポピュラーなUIですよね。
iPhoneユーザーであれば一度は見たことがあるはずです。

Objective-Cを使ったネイティブアプリ開発では「Pull Refresh」とか「Pull To Refresh」とか呼ばれてますが、Wink Toolkitでは「Scroll To Refresh」というようです。

デモはこちら(Winkのデモページの一部です。スマホのブラウザ推奨)


使い方は、
http://www.winktoolkit.org/documentation/symbols/wink.plugins.ScrollToRefresh.html
を参照して下さい。

詳しいサンプルコードがあるので、分かりやすいかと。
文言やローディングの指定はお好みで。
サンプルでは引っ張った時と離した時の2つのコールバック関数はダミーになってます。

もしくは、デモの該当するページのソースを見るのも良いでしょう。


2つ紹介するだけでもそれなりの分量になってしまいましたが、まだまだイケてるエフェクトはたくさんあるので、自分で色々探してみてください。
個人的に「無駄にカッコイイ」と思うのは、SharingWheelというエフェクトかなぁ。

次回は、PhoneGapの導入を紹介したいと思います。

実践(?)Clojure「マルチメソッド」

こんにちはこんにちは!!

清水です。今回は入門Clojureじゃなくて、Clojureのちょっと実践的な機能を紹介します。飽きたわけじゃないんだからねっ!

ClojureはJVMで動いているので、Javaのクラスが使えます。実際、ClojureのデータはJavaのオブジェクトになっています。Javaではクラスを継承したりインタフェースを実装して、メソッドをオーバーライドすることにより、値によって異なる処理を実装します。

Clojureで値によって異なる処理を実装したい場合、同じようにクラスを作ることもできますが、その他にも「プロトコル」や「マルチメソッド」等いくつかの手段が用意されています。今回はその「マルチメソッド」を紹介します。

マルチメソッドとは

マルチメソッドは複数の関数をまとめたような特殊な関数で、引数の型などによって実行時に処理を変えることができます。また、後から簡単に処理を追加することもできます。

Javaのオーバーロードに似ていますが、Javaの場合はコンパイル時の型によって呼び出されるメソッドが決まるのに対して、マルチメソッドは実行時に実際の値によって呼び出されるメソッドが決まります。この特性はどちらかというと通常のメソッドに近いものですが、マルチメソッドは複数の引数を基にメソッドを切り替えることができ、さらにClojureのマルチメソッドは型以外の情報を使うことができます。(パターンマッチにも似ているかも)

Clojureの他に、Common Lispでもマルチメソッドが採用されています。Common Lispの場合は型(か値そのもの)でしかディスパッチできませんが、その分高速な気がします。あとだいぶ前にRubyで実装してみたりしました。Pythonにもライブラリがあるみたいです。

使ってみる

とりあえずどういうものか使ってみましょう。

(defmulti format-item (fn [item] (:type item)))

(defmethod format-item :book [book]
  (str (:title book) "/" (:author book)))

(defmethod format-item :shoe [shoe]
  (str (:manufacturer shoe) "/" (:name shoe) "(" (:color shoe) ")"))

上記のコードはformat-itemというマルチメソッドを定義しています。商品の情報をひとつのマップとして受け取り、種別(:type)に合わせた文字列を構築して返すようになっています。マルチメソッドは通常の関数と同じように呼び出すことができます。

(format-item {:type :book :title "Land of LISP" :author "Conrad, M.D. Barski"})
; => "Land of LISP/Conrad, M.D.Barski"

(format-item {:type :shoe :manufacturer "Converse" :name "ALL STAR MULTI-PIPES OX" :color "Black"})
; => "Converse/ALL STAR MULTI_PIPES OX (Black)"

このような結果になります。

defmultiはマルチメソッドを定義するマクロです。以下のような形式になっています。

(defmulti 関数名 ディスパッチ関数)

「関数名」はdefnと同じで定義する関数の名前です。

「ディスパッチ関数」は上記では

(fn [item] (:type item))

です。

これは呼び出すメソッドを決める際に、引数のどの情報を使うかを決める関数です。format-itemはマップをひとつ受け取ることを想定しているので、その中の:typeのキーに対応する値によって呼び出されるメソッドが決まるということになります。

defmethodはdefmultiで定義したマルチメソッドに対してメソッドを追加するマクロです。

(defmethod マルチメソッド ディスパッチ値 [引数] 本体)

ひとつ目の引数として、処理を追加するマルチメソッドを指定します。

「ディスパッチ値」はdefmultiのディスパッチ関数の戻り値に対応する値を指定します。ディスパッチ関数の戻り値がこの値と一致している場合にこのメソッドが呼び出されます。

defmethodの引数はディスパッチ関数の引数と対応している必要があります。「本体」は通常の関数と同じように書きます。

先ほどのformat-itemのディスパッチ関数は受け取ったマップの:typeの値を返すので、

{:type :book ...}

というマップを渡した場合には

(defmethod format-item :book ...)

で定義したものが呼び出されます。

ちなみにキーワードは関数として呼び出すことができるので、先ほどのdefmultiは以下のように書くこともできます。

(defmulti format-item :type)


複数の引数によるディスパッチ

ディスパッチ関数は複数の引数を受け取ることができます。これによりいくつかの値の組み合わせによって柔軟に処理を切り替えることができたりするかもしれません。

(defmulti calc-attr (fn [attack-attr target-attr damage] [attack-attr target-attr]))

(defmethod calc-attr [:water :fire] [attack-attr target-attr damage]
  (* 2 damage))

(calc-attr :water :fire 100) ; => 200

RPGとかでよくある属性によるダメージ補正をするマルチメソッドです。みっつの引数attack-attr(攻撃属性)、target-attr(対象の属性)、damage(基本ダメージ)を受け取って、補正後のダメージを返します。

ディスパッチ関数はattack-attrとtarget-attrのふたつを要素に持つベクターを返します。defmethodのディスパッチ値も同じようなベクターにすることで、ふたつの値によってメソッドを変えることができます。

defmethodで定義したメソッドはディスパッチ値として

[:water :fire]

というベクターを持ちます。これにより、attack-attrが:water、target-attrが:fireで呼び出された場合にdamageが2倍になります。

こういう場合は属性テーブルとか作った方がわかりやすいかもしれません。複数の引数を使ったディスパッチ関数は個人的にあまり使わないため、良い例が浮かびませんでした。関数を返す関数を作ったり、関数を値に持つマップを作ったりした方が柔軟で分かりやすくなるような気がします。

デフォルトメソッド

先ほどのcalc-attrをどのメソッドにも該当しないように呼び出した場合

(calc-attr :normal :ice 60)
java.lang.IllegalArgumentException: No method in multimethod 'calc-attr' for dispatch value: [:normal :ice]

このようなエラーが発生します。ディスパッチ関数が[:normal :ice]を返したけど、それにマッチするメソッドがないというエラーです。

どれにもマッチしない場合に呼び出されるメソッドを作りたいことは頻繁にあります。そういう場合にはdefmethodのディスパッチ値に:defaultというキーワードを指定します。

(defmethod calc-attr :default [attack-attr target-attr damage]
  damage)

こうしておけば、該当するメソッドがない場合にこのメソッドが呼ばれるようになります。

(calc-attr :normal :ice 60) ; => 60

もし、ディスパッチ値として:defaultを使いたい場合は、defmultiのオプションでデフォルトメソッドのディスパッチ値を変えることができます。

(defmulti xyzzy (fn [x] (:type x))
  :default nil)

こうすると、ディスパッチ値としてnilを指定したメソッドがデフォルトメソッドになります。nilでもディスパッチしたいときには名前空間付きのキーワードを使ったり、一時的なオブジェクトを使ったりもできます。

継承みたいなもの

最初の例のformat-itemのようなマルチメソッドを作ったときに、comicのようなタイプをbookとして扱いたいことがあると思います。そういうときにはderiveという関数を使い、親子関係を構築します。

(defmulti format-item (fn [item] (:type item)))

(defmethod format-item ::book [book]
  (str (:title book) "/" (:author book)))

(derive ::comic ::book)

(format-item {:type ::comic :title "Kodoku no gurume" :author "Masayuki Qusumi and Jiro Taniguchi"})
; => "Kodoku no gurume/Masayuki Qusumi and Jiro Taniguchi"

deriveに子となるキーワードと親となるキーワードを指定すると、メソッドの決定時に自動的に子を親として扱ってくれます。deriveに渡すキーワードは名前空間を持つ必要があります。

子にはキーワードの他に、名前空間付きのシンボルと、Classオブジェクトを指定することができます。親には名前空間付きのキーワードとシンボルしか指定できません。

特定のマルチメソッドでのみの親子関係を使いたいときには、make-hierarchyという関数で階層オブジェクトを作り、そのRefをdefmultiに渡します。

(def hierarchy (ref (make-hierarchy)))

(defmulti xyzzy (fn [item] (:type item))
  :hierarchy hierarchy)

(dosync (alter hierarchy derive ::comic ::book))

階層に親子関係を追加するときにはderiveに引数として渡します。この場合のderiveは親子関係を追加した新しい階層オブジェクトを返します。それを再度Refの値にしなければマルチメソッドに反映されません。そのため上記のコードではdosyncとalterを使ってRefを更新しています。


マルチメソッドの説明は大体以上です。マルチメソッドだけでも便利ですが、メタデータと組み合わせたり、マクロの展開時にマルチメソッドを呼び出したりすると面白いことができそうな気がします。

Friday, June 22, 2012

Clojure入門「データ構造」

こんにちは。清水です。

今回はClojureのデータ型についていくつかだらだら解説します。

ClojureのデータにはJavaの標準なクラスのインスタンスになっているものがいくつかあります。なにがどんなクラスのインスタンスなのかは値をclass関数に渡したりするとわかります。

列挙系はダレますね。。。

文字列

Clojureの文字列はJavaのStringオブジェクトです。

"Hello, world."

ダブルクォーテーションで囲んだものが文字列になります。str関数を使うとオブジェクトを文字列に変換できます。

(str 428) ; => "428"

strは複数の引数を受け取り、全てを結合した文字列を返します。

(str "Hello" "World") ; => "HelloWorld"

clojure.stringという名前空間にいくつか文字列用の関数が用意されています。Stringのメソッドを使うこともできます。

文字

文字列とは別にひとつの文字を表すデータ型もあります。

\A

文字の前にバックスラッシュを付けると、その文字のリテラルになります。これはJavaのCharacterオブジェクトです。

\space ; 半角スペース\newline ; 改行\tab ; タブ

など、特殊な文字を表すリテラルもあります。

数値

数値には整数と浮動小数点数と分数があります。

4287.222/3

整数は値の大きさによってInteger、Long、BigIntegerオブジェクトのいずれかになります。浮動小数点数はDoubleオブジェクトで、分数はClojure独自のクラスのインスタンスです。また、分数のリテラルは/の前後にスペースを入れることはできません。

ブール

ブール値はJavaのBooleanオブジェクトで、trueとfalseの二つの値があります。ifの条件式や、コレクションをフィルタリングする時に渡す関数の戻り値などに使われます。ただし、その場合は厳密にtrueかfalseである必要はなく、falseと(後述する)nil以外は全てtrueとして扱われます。0や空の文字列や空のリストなどはfalseにはなりません。

nil

nilは何もないことを表す値です。内部的にはJavaのnullです。nilはfalseとして扱われる他、空のコレクションにもなります。

ちなみに、空のリストはnilではありません。

キーワード

キーワードは名前空間と名前を持つオブジェクトです。文字列にも似ていますが、同じ名前空間と名前を持つキーワードは同じインスタンスになるため、文字列よりも比較が速い等の利点があります。

:foo

上記のようにコロンの後に名前をつけると、その名前のキーワードになります。この形式のキーワードは名前空間を持ちません。そのため、どの名前空間内で記述しても同じインスタンスを表すことになります。

::foo

このようにコロンを二つ付けると、現在の名前空間のキーワードになります。

:xyzzy/foo

また、上記のように名前空間と名前をスラッシュで区切ると、任意の名前空間のキーワードを使うことができます。

キーワードはマップのキーに使われることが多いです。ほとんどの場合、名前空間を持たないキーワードが使われ、名前空間毎に固有のキーを用意したい場合などに名前空間付きのキーワードを使ったりします。

(Rubyのシンボルに近いのかも。)

シンボル

シンボルはキーワードと同じように名前空間と名前を持ち、コード上でなにかの名前を表すのに使われます。変数や関数の名前、strやprintlnなどの名前もシンボルです。シンボルはそのまま評価すると結びつけられた値を返します。そのため、

str

と書けばstrの関数自体が返されます。定義されていないシンボルを評価しようとすると

java.lang.Exception: Unable to resolve symbol: foo in this context

などといったエラーが発生します。シンボルを直接値として扱うには、名前の前にシングルクォーテーションを付けます。

'foo

このシンボルは名前空間を持ちません。シングルクォーテーションの代わりにバッククォーテーションを付けると、現在の名前空間内のシンボルになり、キーワードと同じように名前の前に名前空間を付けると任意の名前空間のシンボルになります。

ちなみにこのシングルクォーテーションはquoteというマクロの糖衣構文で、下のコードと同じになります。

(quote foo)

quoteは引数を評価せずに式としてそのまま返します。

シンボルはマクロを書くときに使います、それ以外では使う機会は少ないです。

ちなみに、キーワードはシンボルではなく、シンボルはキーワードではありません。

リスト

Clojureのリストは連結リストとして実装されたオブジェクトです。JavaのListインタフェースを実装しているため、Javaのメソッドに渡すこともできます。リストは丸括弧に値を並べた形で表されます。

'(15 9 48)

リストを作るときには先ほどのquoteを使います。quoteしないと上記の場合は15という関数を呼び出すという意味になってしまいます。上記のように書いた場合は括弧の中のものもquoteされてしまうので、変数の値をリストに含めようとするとシンボルになってしまいます。変数の値を含めたい場合はlist関数を使います。

(list foo bar xyzzy)

この場合は変数foo、bar、xyzzyの値を含んだリストになります。空のリストの場合のみ、quoteもlistもなく、()と書くだけで作れます。

リストの先頭の要素はfirst関数、先頭を除いたリストはrest関数で取得できます。

(first '(15 9 48)) ; => 15(rest '(15 9 48)) ; => (9 48)

ベクター

ベクターは整数のインデックスでアクセスできるコレクションです。JavaのVectorとは違うものですが、Listインタフェースを実装しています。ベクターは[]の中に要素を並べて書きます。

[15 9 48][foo bar xyzzy]

ベクターを作るときにはquoteする必要はなく、変数もそのまま書けます。

要素を取得するにはget関数を使います。

(get [15 9 48] 1) ; => 9

該当する要素が無い場合はnil、またはgetの3番目の引数を返します。また、ベクターそのものを関数として呼び出しても要素を取得することができます。

([15 9 48] 1) ; => 9

この形式の場合、該当する要素が無い場合はエラーになります。

マップ

マップは順序を持たない連想配列です。{}の中にキーと値を並べて書きます。

{:name => "Hamachi" :price => 130}

上記の場合はnameとpriceという名前のキーワード二つをキーに持つマップになります。

マップの要素を取得するのにもget関数が使えます。

(get {:name => "Hamachi" :price => 130} :price) ; => 130

ベクター同様に、マップそのものを関数として呼び出して要素を取得することもできます。また、キーワードかシンボルを関数としてマップを渡しても同様のことができます。

({:name => "Hamachi" :price => 130} :price) ; => 130(:price {:name => "Hamachi" :price => 130}) ; => 130

この形式の場合でも該当する要素が無い場合はnilを返しますが、関数として呼び出せないものを左側に置いてしまうとエラーになるので注意が必要です。

マップはJavaのMapインタフェースを実装しているので、メソッドの引数に渡したりできます。Javaで書くよりも簡単。


セット

セットは順序を持たず重複の無い要素の集合です。JavaのSetインタフェースを実装しています。セットは#{}の中に要素を並べて書きます。

#{10 20 30}

セットにもget関数が使えます。引数として要素を渡すと、その要素がセットの中に含まれる場合には同じものを返し、無い場合はnil(または3番の引数)を返します。

(get #{10 20 30} 20) ; => 20

セット自体を関数として呼び出しても同じことができます。

(#{10 20 30} 20) ; => 20

要素がある場合には要素そのものを、無い場合はnilを返すので、セットがfalseやnilを含むことが無ければ条件式として使うことができます。

上記のリストやベクターなどのコレクションは全て変更不能なオブジェクトです。ベクターの要素を変えたい場合は要素を変えた新しいベクターを作成します。Clojureにはその為の関数が用意されています。次回はコレクションについてもうちょっと解説する予定です。

なにやら新しいシリーズが始まったようなので合わせてどうぞ

Thursday, June 21, 2012

Wink Toolkit入門 ~導入編~

こんにちは&はじめまして。
ニャンパス一味の土井(@exfreeter)です。
まだまだ駆け出しのプログラマですが、iOS向けアプリやClojureによるWebアプリをメインに開発しています。

このたび、清水君(@yuushimizu)のClojureの連載と並行してNyampass tech blogで記事を執筆することになりました。
慣れてくると言葉使いが崩れて関西弁になると思いますが、生暖かい目で見守って頂けると幸いです。

僕はObjective-CでiOS向けアプリを作ることが多いんですが、Javascriptで作りたいという方は多いと思います。
そこで、まずは「スマホ向けネイティブアプリをJavaScriptで作る」というテーマで書いていく予定です。(数回程度の予定)
これらの一連の記事を読めば、初歩的なネイティブアプリをHTML/CSS/JSだけで作れるようになるかも。

今回は、スマホWebサイト用のJavascriptフレームワークであるWink Toolkitの紹介および導入を解説したいと思います。
次回以降、Winkの豊富なエフェクトの紹介やPhoneGapによるWebサイトのネイティブ化などに進んでいく予定です。

では、早速はじめましょう。


Wink Toolkitとは?

一言でいうと、スマホ向けのサイトを作るためのJSフレームワークです。
似たようなものとしてjQuery MobilejQTouchなどがありますが、そのようなものだと思っていただければOKです。

恐らく、2012/06現在で一番勢いがあるのはjQuery Mobileだと思います。
ネット上の情報や関連書籍なども充実してます。

が、現状だと重いんですよね.....。
ニャンパスでも幾つかのフレームワークを試しましたが、結局はパフォーマンスを重視してWink Toolkitに落ち着きました。

Wink Toolkitの特徴は、軽さと豊富なエフェクト系UIです。
デモサンプルも豊富なので、これらを組み合わせるだけでスマホぽいUIのサイトが作れちゃいます。
具体的にどういうことが出来るかは、以下のURLから見ることが出来ます。(PCから見ても良いですが、スマホのブラウザで見るのをオススメします)


FacebookやPathのiOSネイティブアプリで見られる横スライド式のメニューや、スマホでは定番の「引っ張って更新」なんかもありますよね。
正直、「無駄にカッコいい」というレベルです。

Wink Toolkitの導入

では、早速Wink Toolkitを導入していきましょう。

まず、本家サイトからWink本体をダウンロードして下さい。(画面上部のdownloadsからダウンロードページに行けます)
とりあえず、ダウンロードページの一番上のリンクから最新版のZipを落としておいてください。(2012/06/21現在での最新版は1.4.2。以下、1.4.2を前提に進めます)

そのZipが上記デモページのソースになっているので、自信のある方はそれを見てゴニョゴニョやってるうちに使いこなせるようになるかと。

今回は初回ということで、デモの一番上にあるアコーディオンのページのソースを見るところまでにしましょう。

Zipを解凍したら、ui/layout/accordion/test/test_accordion_1.htmlをブラウザで開いてみてください。
3つのセクションを持つアコーディオンがありますね。
では、test_accordion_1.htmlのソースを見ていきましょう。

8行目と11行目でCSS、14行目でテーマ用のJS、19行目から36行目まででWinkのcoreのJS、39行目でアコーディオン用のJSをそれぞれ読み込み、41行目以降がこのページで使うアコーディオン等を具体的に作っているJSのコードになっています。
これが、Wink Toolkitの基本的な使い方になります。
また、core部分はwink.min.jsで代替可能です。(今回だとwink_1.4.2.min.jsですね)

CSSとJSがペアになっている点に注目して下さい。(8/14行目、11/39行目)
Wink Toolkitを使う場合、基本的に、hogehoge.jsを使う時にはhogehoge.cssも読み込む必要があります。
例えば、アコーディオンを使いたい時はaccordion.jsとaccordion.cssが必要だし、カルーセルを使いたい時はcarousel.jsとcarousel.cssが必要になります。
CSSを忘れることが多いので、注意して下さい。

以上、駆け足になりましたが、Wink Toolkitの導入でした。
詳しくは、本家のドキュメントを参照して下さい。
かなり分かりやすいと思います。

次回は、Wink Toolkitの豊富なエフェクトUIの中でも一際光る(と個人的には思っている)Facebookライクな横スライド式のメニューを紹介しようと思います。

Friday, June 15, 2012

Clojure入門「関数と変数」

こんにちは。ニャンパス一味の清水です。

今回はClojureの関数と変数について少しだけ解説します。

関数

関数の定義

前回も書きましたが、関数は"defn"というマクロを使って定義します。

(defn -main []
  (println "Hello, world!"))

defnの形式は以下のようになっています。

(defn 関数名 [引数 ...] 本体)

先の例の場合は関数名が-main、引数は無し、本体は(println "Hello, world!")です。

引数を取る場合には[]の中に引数の名前を並べます。

(defn add-forty-two [x]
  (+ 42 x))

(defn triangle [base height]
  (/ (* base height) 2))

それぞれ、引数をひとつ受け取って42を足した数を返す関数と、引数をふたつ受け取って三角形の面積を返す関数です。

関数の呼び出し

関数は以下のような形式で呼び出します。

(関数 引数 ...)

コードの中では括弧の中の一番左が関数かマクロになり、その右側に引数が並びます。上記で定義したadd-forty-twoを呼び出すには以下のようにします。

(add-forty-two 30)

この場合、add-forty-twoの定義内のxには30が束縛され、結果として72を返します。同様に、triangleを呼び出すには以下のようにします。

(triangle 31 6)

引数baseには31、heightには6が束縛され、93を返します。

ちなみに、Clojureのコードでは区切り文字としてスペースやタブや改行が使用されます。カンマを付けて見やすくすることもできますが、動作には影響がありません。

また、add-forty-twoやtriangleの本体でも関数を呼び出しています。

(+ x 42)

上記は + という関数を、xと42を引数として呼び出しています。

(/ (* base height) 2)

triangleではふたつの関数 / と * を呼び出しています。この場合、先に

(* base height)

が評価され、その結果と2が / の引数になります。

Javaメソッドの呼び出し

ClojureからJavaのメソッドを呼び出すことができます。Clojureの文字列はJavaのStringオブジェクトなので、Stringクラスのメソッドを使えます。

(.toLowerCase "HELLO")

上記は"HELLO"というStringオブジェクトのtoLowerCaseメソッドを呼び出しています。結果として"hello"が返ります。このように、メソッド名の前に . を付けたものを関数として呼び出すと、ひとつ目の引数のメソッドが呼び出されます。

無名関数

"fn"というマクロを使うと無名関数を作ることができます。

(fn [x] (+ 42 x))

これは先ほどのadd-forty-twoと同じ結果を返す関数です。無名関数はそのまま呼び出せるので

((fn [x] (+ 42 x)) 30)

このように、関数を置く場所に無名関数を置くことができます。(この場合意味はありませんが)

無名関数は、関数を受け取る関数を呼び出す場合などに使います。

変数 

グローバル変数

変数は"def"という特殊な形式を使用して定義できます。

(def 名前 値)

defは名前空間内でグローバルな変数を作ります。

(def x 42)

(defn add-forty-two [y]
  (+ x y))

上記の場合、add-forty-two内のxは42になります。

ローカル変数

defはグローバルな変数を作るため、関数内のローカルな変数を作る時には使用できません。ローカル変数を作るには"let"というマクロを使います。

(let [変数名 値 ...] 処理)

letで作った変数はletの中でのみ参照できます。

(let [x 42]
  (+ x 30))

上の例では + に42と30が渡されます。これを実行すると(let ...)という式自体が72を返します。(関数を作ったりはせずに)

ひとつのletで複数の変数を作ることもできます。

(let [x 42
      y 30]
  (+ x y))

これは先の例と同じ動作をします。

letで作った変数には別の値を代入できません。Clojureでは変数の再代入をしなくても大体の処理を書くことができます。再代入可能な変数が必要な場合は別の仕組みを使いますが、今回はスルーします。

関数と変数

defnは無名関数を値としてグローバル変数を定義しているだけなので、defを使って書き直すことができます。

(defn add-forty-two [x]
  (+ 42 x))

(def add-forty-two (fn [x] (+ 42 x)))

上記のdefnとdefは同じ結果になります。

つまり、Clojureでは関数も数値も同じようにデータとして扱えるということです
(JavaScriptと同じ感じです)

letを使ってローカルな関数を作ることもできますし、そのために"letfn"というマクロも用意されています。関数に関数を渡すことも多く、標準で用意されている関数には、リストやベクターを処理するために関数を受け取るものが多く存在します。

次回はその辺りも踏まえて、データ構造についてちょっぴり解説していこうと思います。