2012年7月8日日曜日

アプリケーションの実装(2)

はじめに

おはようございます。当ブログにアクセス頂き、ありがとうございます。
『臥薪嘗胆』が高校時代の座右の銘だった、たなけんです。
本エントリでは、アプリケーションの実装について記載いたします。

アプリケーションの実装(2)

本日は以下のコードを紹介します。
  • url構築関数
  • xml取得関数
  • JSON文字列出力関数
  • 意味検索メイン関数

url構築関数

(defn build-url
"to build url string with parameters"
[host vid q type site]
(apply str "http://" host "/v001?vid=" vid "&q=" q "&type=" type "&site=" site))
view raw core.clj hosted with ❤ by GitHub

引数に与えられた文字列をurlテンプレートに埋め込みます。
Dictionary.comは意味取得以外にもAPIを提供しているため、
柔軟にurlを構築出来るよう引数を設定しています。 
  • str関数: 引数を文字列として結合する

xml取得関数

(defn extract-xml
"to send request and extract xml part from reply"
[url]
(:body (try
(http-client/get url)
(catch java.io.IOException ioe (handle-exception ioe)))))
view raw core.clj hosted with ❤ by GitHub

引数のurlへhttpリクエストを送信し、レスポンスを受信します。
レスポンスのbody要素をxml文字列として取得します。

  • clj-http.client/get関数: 引数のurlにGETメソッドのhttpリクエストを送信

JSON文字列出力関数

(defn generate-json
"to return a hashmap including word and entries"
[xml-string]
;; local function to call xpath/$x safely
(letfn [(safety-xpath-call
[xpath xml]
(try
(xpath/$x xpath xml)
(catch org.xml.sax.SAXException saxe (handle-exception saxe))))] ; end of letfn safety-xpath-call
(let [word (:query (:attrs (first (safety-xpath-call "//dictionary" xml-string))))
entries (let [pos (for [line (safety-xpath-call "//partofspeech" xml-string)] (:pos (:attrs line)))] ; end of let pos
; local function to extract mean from //partofspeach/defset/def
(letfn [(get-mean
[each-pos xml-string]
(for [line (safety-xpath-call (str "//partofspeech[@pos=\"" each-pos "\"]/defset/def") xml-string)] (:text line)))] ; end of letfn get-mean
; generating entries
(for [i pos]
(for [j (get-mean i xml-string)] {:pos i :mean j}))))] ; end of let word, entries
; get-dictionary-define-json (cont.)
(json/generate-string {:word word :entries (flatten entries)}))))
view raw core.clj hosted with ❤ by GitHub

xml文字列から、単語、品詞、意味を含むJSON文字列を生成します。

  • letfn特殊形式: スコープ内でのみ利用する関数を束縛
  • clj-xpath.core/$x: 第1引数のxpath問い合わせを、第2引数のxmlに対して実行する
  • let特殊形式: スコープ内でのみ利用する変数を束縛
  • for特殊形式: リスト内包表記
  • clj-json.core/generate-string: ClojureオブジェクトからJSON文字列を生成する

例えば下記のようなxmlから
<?xml version="1.0" encoding="UTF-8" ?>
<dictionary query="test" totalresults="1">
<entry source="pdict" id="4162883">
<display_form ><![CDATA[test]]></display_form>
<pron><![CDATA[[test]]]></pron>
<partofspeech pos="noun">
<defset>
<def charcnt="68" defno="1"><![CDATA[the means used to determine the quality, content, etc., of something]]></def>
<def charcnt="42" defno="2"><![CDATA[examination to evaluate a student or class]]></def>
</defset>
</partofspeech>
<partofspeech pos="verb (used with object)">
<defset>
<def charcnt="20" defno="3"><![CDATA[to subject to a test]]></def>
</defset>
</partofspeech>
</entry>
</dictionary>
view raw test.xml hosted with ❤ by GitHub

下記のようなJSONを作成します。(改行、エスケープシーケンスは若干異なります)
{
"word":"test",
"entries":[
{"pos":"noun",
"mean":"the means used to determine the quality, content, etc., of something"},
{"pos":"noun",
"mean":"examination to evaluate a student or class"},
{"pos":"verb (used with object)",
"mean":"to subject to a test"}
]
}
view raw result.json hosted with ❤ by GitHub


追記: リスト内包表記について

例えば、(for [i '(1 2 3)] (+ 10 i))を評価すると、(11 12 13)が結果として返されます。
動作のイメージとしては、forの後の角括弧内のiにリストの各要素が束縛され、角括弧の後のS式を評価した結果のリストを返すといったものです。
1変数の場合は、map関数を用いても同様の結果を得ることが出来ます。
(map #(+ 10 %) '(1 2 3))

forの場合は複数の変数や、階層を持たせる(入れ子のfor文のイメージ)ことができます。
複数の変数を利用すると、Pythonのタプルのような、2要素のhashmapを簡単に作ることができます。

複数変数の例 (for [i '(:a :b :c) j '(1 2 3)] {i j})
結果 ({:a 1} {:a 2} {:a 3} {:b 1} {:b 2} {:b 3} {:c 1} {:c 2} {:c 3}) 

入れ子の例
(for [i '(:a :b :c)]
  (for [j '(1 2 3)] {i j})
)
結果

(
  ({:a 1} {:a 2} {:a 3})
  ({:b 1} {:b 2} {:b 3})
  ({:c 1} {:c 2} {:c 3})
)


今回の作業は以上。最後までお読み頂きありがとうございました。
たなけん (作業時間45分)

0 件のコメント:

コメントを投稿