Hatena::ブログ(Diary)

Kumappusの日記 このページをアンテナに追加 RSSフィード

2011-12-24 ScalaでXMLをパーズする このエントリーを含むブックマーク

ネット上にはなんていうか「試しにやってみました」程度の簡単な例しか転がっていないので多少複雑な例を。以下のようなXML(sample-1.xmlとする)があったとして

<?xml version="1.0" encoding="utf-8" ?>
<ninjas>
  <ninja id="ninjaslayer">
    <attacks>
      <attack name="Suriken">
        <damage>10</damage>
        <times>1000</times>
      </attack>
      <attack name="Meia lua de compasso">
        <damage>50</damage>
        <times>30</times>
      </attack>
      <attack name="Tsuyoi suriken">
        <damage>100</damage>
        <times>2</times>
      </attack>
      <attack name="Evil Black Fire">
        <damage>500</damage>
        <times>1</times>
        <condition>Controlled by Naraku</condition>
        <condition>Unconsious</condition>
      </attack>
      <attack name="Sacred Nunchaku">
        <damage>1000</damage>
        <times>1</times>
        <condition>Synchronized with Naraku</condition>
        <condition>Clutch Situation</condition>
      </attack>
    </attacks>
    <name>Ninja Slayer</name>
  </ninja>
  <ninja id="forest-sawatari">
    <attacks>
      <attack name="Machete">
        <damage>50</damage>
        <times>10</times>
      </attack>
      <attack name="Bio takeyari">
        <damage>50</damage>
        <times>10</times>
      </attack>
    </attacks>
    <name>Forest Sawatari</name>
  </ninja>
  <ninja id="frogman">
    <attacks>
      <attack name="Bio Frogs Tongue">
        <damage>50</damage>
        <times>100</times>
      </attack>
      <attack name="Makimono Jitsu">
        <damage>300</damage>
        <times>30</times>
      </attack>
    </attacks>
    <name>Frogman</name>
  </ninja>
  <ninja id="frogman">
    <attacks>
      <attack name="Bio Frogs Tongue">
        <damage>50</damage>
        <times>100</times>
      </attack>
      <attack name="Makimono Jitsu">
        <damage>300</damage>
        <times>30</times>
      </attack>
    </attacks>
    <name>Frogman</name>
  </ninja>
  <ninja id="genocide">
    <attacks>
      <attack name="Buzzsaw">
        <damage>500</damage>
        <times>100</times>
      </attack>
    </attacks>
    <name>Genocide</name>
  </ninja>
  <ninja id="evolver">
    <attacks>
      <attack name="Ochoko Defence">
        <damage>1</damage>
        <times>100</times>
      </attack>
      <attack name="Ju Jitsu">
        <damage>50</damage>
        <times>100</times>
      </attack>
      <attack name="Evolution">
        <damage>150</damage>
        <times>100</times>
      </attack>
      <attack name="Supersize">
        <damage>150</damage>
        <times>10</times>
      </attack>
      <attack name="Monsterize">
        <damage>750</damage>
        <times>1</times>
        <condition> Supersized </condition>
      </attack>
    </attacks>
    <name>Evolver</name>
  </ninja>
</ninjas>

ニンジャ名の一覧をStringリストで取りたい。どうするか。

import scala.xml._
val xm = XML.loadFile("sample-1.xml")

で、変数xmにXMLとして取り込んだとする。

単純に

xm \\ "name"

とすると結果は

NodeSeq(<name>Ninja Slayer</name>, <name>Forest Sawatari</name>, <name>Frogman</name>, <name>Frogman</name>, <name>Genocide</name>, <name>Evolver</name>)

となるので

for(x <- xm \\ "name" ) yield x.text 

としてあげるとこの式の値が

scala.collection.immutable.Seq[String] = List(Ninja Slayer, Forest Sawatari, Frogman, Frogman, Genocide, Evolver)

となり目的達成。

Scalaのfor文は強力なのでこのようにXMLのパーズと組み合わせるとなかなか威力を発揮するようだ。

2011-12-15 multipart/form-dataのバウンダリ文字列生成(その後) このエントリーを含むブックマーク

この方がClojureっぽいよ!という指摘をいただきました。

github:gist

char-rangeのところはおいといて(w

確かに昨日のソース

(defn generateBoundary []
  (let [chars "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"]
    (apply str "-------" (take 40 (repeatedly #(rand-nth chars))))))

にしたほうがよりClojureっぽいですね。

(rand-nth [coll])

はコレクションcollの中からn番目をランダムに選ぶ関数

(repeatedly f)

関数fを無限回呼び出す遅延シーケンスを返す関数

#(rand-nth [coll])

として無名関数化してrepeatedlyに渡している。

ただしこいつは

(repeatedly n f)

と書いて無限シーケンスの先頭要素n個を取ることもできるので

(defn generateBoundary []
  (let [chars "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"]
    (apply str "-------" (repeatedly 40 #(rand-nth chars)))))

のようにtakeを省略できる。

乱数については(rand-nth [coll])以外にも(rand-int n) (0〜n-1までの整数乱数を返す)のもあるのでJavaのRandomのオブジェクトを生成して使わないでもよいケースも多そう。

2011-12-14 Scalaでmultipart/form-dataのバウンダリ文字列生成 このエントリーを含むブックマーク

multipart/form-dataでデータアップロードする必要が出てきたのでScalaでこりこり書き始めた。

パートの切れ目を表すboundary文字列を生成するプログラムとしてランダム文字列の生成を見つけた。

以下ソースコード引用

private String generateBoundary() {

String chars = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_";
Random rand = new Random();
String boundary = "";
for (int i = 0; i < 40; i++) {

int r = rand.nextInt(chars.length());
boundary += chars.substring(r, r + 1);
}
return "---------------------------" + boundary;
}

これをほぼそのまま移してもScalaで動くんだが、せっかくなので多少関数型っぽくする。

private def generateBoundary :String = {
    val chars = (('0' to '9') ++ ('a' to  'z') ++ ('A' to 'Z' )).mkString + "-_"
    val rand = new scala.util.Random
    "----------" + (for( i <- 0 until 40 ) yield chars(rand.nextInt(chars.length))).mkString
}

Scalaのfor文はなかなか強力。こうやって「再代入可能な変数を減らしていく」わけですな。

選択される文字一覧には範囲型の結合を使ってみたが、これはまあおまけ。

ついでにClojureでも書いてみたらこんな感じか。

(defn generateBoundary []
  (let [chars "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"]
       [rnd (new java.util.Random)]
    (str "-------" (apply str (for [i (range 40)]
       (nth ccc (. rnd nextInt (count ccc))))))))

ふむ、関数型っぽく書いたScalaと同じ骨組みがどこか透けて見えるじゃないですか。

2011-10-08 Clojure ロゴをアイコンに貼り付ける (1.3.0対応) このエントリーを含むブックマーク

Clojure 1.3.0では動かなくなったので

以下の変更が必要

(use '(clojure.contrib java-utils))

(use 'clojure.java.io)

に。1.3.0ではcontribは本体に取り込まれて名前が変わってる。

2011-10-06 前回の答え このエントリーを含むブックマーク

http.request(POST){
    uri.path = 'mform.html'
    body = [status : 'update!', source : 'gegegege']
    requestContentType = groovyx.net.http.ContentType.URLENC
    response.success = { resp ->
        println("status: ${resp.statusLine}")
        assert resp.statusLine.statusCode == 200
    }
}

http.request(POST,TEXT){ req ->
    uri.path = 'mform.html'
    println(uri.path)
    send URLENC, [status : 'update!', source : 'gegegege'] // will be url-encoded
    response.success = { resp, xml ->
        println("status: ${resp.statusLine}")
        assert resp.statusLine.statusCode == 200
    }
}

でうまく行った。ところで書いてて疑問だったんだけど、ここのuriとかsend URLENCとかってどこで定義されてるんだろう。一種のDSLになってるんでHTTPBuilderのどこかにあると思うんだけど。