SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

プログラミング言語「Nu」入門

プログラミング言語NuでLispプログラミング
~S式の使い方~


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=") になります。
  • % echo -n 'LOGIN:PASSWORD' | uuencode -m -
    begin-base64 644 -
    TE9HSU46UEFTU1dPUkQ=
    ====
    
  • 上のコードの説明はCocoa-APIの説明になるので、詳細は割愛します。

タイムラインの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式を扱う簡単なプログラムをたくさん書いてみるのが良いと思います。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
プログラミング言語「Nu」入門連載記事一覧

もっと読む

この記事の著者

吉田裕美(ヨシダユウミ)

有限会社 EY-Office 取締役CADのベンチャー企業でCADのコア部分や図面管理システムなどの開発に従事した後、独立しJava,Ruby,PerlでWebアプリを中心に開発してきた。現在は殆どの開発はRuby on Rails。ここ数年はソフトウェアエンジニアの教育に興味をもち、従来の知識偏重な教育ではなく現実の問題を解決できるエンジニアを育てる教育に注力している。またLisp等に関心...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/4835 2010/04/19 19:51

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー