Twitterのタイムラインを取得する
Twitterはご存知のようにAPIを公開しているので、プログラムから簡単に自分のタイムラインを取得できます。Nuは Cocoa(Mac OS Xの)API を呼び出せますので、Cocoaの NSMutableURLRequestクラスを使ってタイムラインを取得します。Cocoa APIの詳細に付いてはMac OS X Reference Libraryで参照できます。Macの開発環境Xcodeがインストールされていれば、Xcodeのヘルプを参照する方が便利かもしれません。
タイムラインの取得
(load "Nu:nu")
(load "Nu:cocoa")
(set basic-auth-token "XXXXXXXXXXXXXXXXX")
(set twitter-url "http://twitter.com/statuses/friends_timeline.xml?count=200")
(function twitter-timeline-xml ()
(let ((http-req (NSMutableURLRequest
requestWithURL:(NSURL URLWithString:twitter-url))))
(http-req setHTTPMethod:"GET")
(http-req setValue:"Basic #{basic-auth-token}" forHTTPHeaderField:"Authorization")
((NSXMLDocument alloc) initWithData:(NSURLConnection
sendSynchronousRequest:http-req
returningResponse:nil error:nil)
options:0 error:nil)))
ここでは、タイムラインをXMLで取得する関数 twitter-timeline-xml を定義しています。
- load 関数はライブラリーの読み込みで、Nu:nu は基本機能を、Nu:cocoa はCocoaライブラリーを読み込んでいます。
- set は広域変数の設定です。Twitter APIはBasic認証(またはOAuth)で認証を行いますのでBasic認証用の文字列を保持しています。この文字列はターミナルで以下のコマンドを使い、自分のログインパスワードをBASE64文字列に変換してください。下の例では (set basic-auth-token "TE9HSU46UEFTU1dPUkQ=") になります。
- 上のコードの説明はCocoa-APIの説明になるので、詳細は割愛します。
% echo -n 'LOGIN:PASSWORD' | uuencode -m - begin-base64 644 - TE9HSU46UEFTU1dPUkQ= ====
タイムラインのXMLをS式に変換する
XMLから情報の抽出はXPathを使うと簡単です。Twitterから取得できるタイムラインのXMLデータ構造等についての説明は省きますので、興味のある方はネット上で調べてみてください。
<statuses>
...
<status>
<id> 7736671823 </id>
<text> Hello! </text>
<in_reply_to_status_id> 7736671819 </in_reply_to_status_id>
...
<user>
...
<screen_name> hoge </screen_name>
...
</user>
...
</status>
...
</statuses>
タイムラインのXMLデータは上記のようになります。以下のコードは screen_name の値を取得し配列で戻します。ここでは xml という変数に入っているXMLデータに対し、nodesForXPath:error: というメソッドを適応しています。
(xml nodesForXPath:"/statuses/status/user/screen_name" error:nil)
S式のデータを使いたいので、以下のようにNuの listメソッドを適用し、配列をS式(リスト)に変換します。
((xml nodesForXPath:"/statuses/status/user/screen_name" error:nil) list)
ただし、nodesForXPath:error:メソッドの戻り値はNSXMLNodeオブジェクトの配列なので、内部で扱いやすい文字列や数値に変換する必要があります。下のコードではタイムラインにおける最初の発言の screen_name の値を文字列で取得できます。
((car ((xml nodesForXPath:"/statuses/status/user/screen_name" error:nil) list)) stringValue)
プログラムの中ではすべての要素に対して stringValue を適応したいですね。このような場合 map関数を使うと便利です。
(function string-list-from-xml (xml path)
(map (do (e) (e stringValue))
((xml nodesForXPath:path error:nil) list)))
map関数は引数に処理関数とリストを受け取り、リストの各要素に処理関数を適応したリストを戻します。上のコードでは (xml nodesForXPath:path error:nil) list) で得られたリストの各要素に (do (e) (e stringValue))ブロックを渡しています。
タイムラインを取得してS式に変換するコードをまとめると以下のようになります。
(function string-list-from-xml (xml path)
(map (do (e) (e stringValue))
((xml nodesForXPath:path error:nil) list)))
(function number-list-from-xml (xml path)
(map (do (e) ((e stringValue) doubleValue))
((xml nodesForXPath:path error:nil) list)))
(function twitter-timeline ()
(let ((xml (twitter-timeline-xml))
(zip4 (number-list-from-xml xml "/statuses/status/id")
(string-list-from-xml xml "/statuses/status/user/screen_name")
(string-list-from-xml xml "/statuses/status/text")
(number-list-from-xml xml "/statuses/status/in_reply_to_status_id"))))
(function zip4 (l1 l2 l3 l4)
(cond ((null l1) ())
(else (cons (list (car l1) (car l2) (car l3) (car l4))
(zip4 (cdr l1) (cdr l2) (cdr l3) (cdr l4))))))
- let はローカル変数を定義するNuのオペレータです、このコードでは xml変数に (twitter-timeline-xml) の値を代入し (zip4 ... を実行しています。
- zip4関数は引数として渡された4つのリストの各要素を並べたリストのリストを作る関数で (zip4 '(a b) '(c d) '(e f) '(g h)) の値は ((a c e g) (b d f h)) になります。
map関数
map関数は引数として関数を受け取り、リストに対しての処理を行います。Lispや近代的な言語では関数(処理)を他の関数に渡したり、関数の戻り値として使えます。また、このような関数を高階関数と呼びます。
さて、このmap関数の定義ですが簡単です。
(function map (f l)
(cond ((null l) ())
(else (cons (f (car l)) (map f (cdr l))))))
- 引数のリストかが空なら空のリスト () を戻します。
- それ以外の場合は (f (car l)) でリストの最初の要素を f に渡された関数に適応した値と、リストの最初以外の値を map で評価した値を cons でつないで結果のリストを作り、map の値として戻します。
- ここでも再帰的な呼び出しが行われています。
まとめ
今回はNuのLisp的なプログラミングについて説明してきました、S式と再帰的呼び出しを使ったLisp的プログラミングはどうだったでしょうか?
Lispを習得するにはS式や再帰的呼び出しに慣れる事が必須です。そのためには、LispでS式を扱う簡単なプログラムをたくさん書いてみるのが良いと思います。
