みどりねこ日記

よくわからないけど、頑張りますよ。

シリコンバレーいってきた

夏のインターンの関係で、シリコンバレー行ってきた。

すごいおいしいみたいな印象があったライスクリスピー、久しぶりに食べたらクッソまずかった。

けど友達にも「美味しいよこれ!!!」って言って買わせた手前、美味しいねこれ!って言いながら食べ続けた。

食事は結構美味しかったんだけど、普段一日一杯ジュース飲むだけみたいな生活送ってたせいかお腹壊した。

しかも3日目くらいに熱が出てつらい目にあった。

GoogleとかFacebookとかEvernoteとか色々訪問したけれど、どの国のどの企業でも開発部門と営業企画部門では雰囲気が全然違うなって。

開発部門は暗い部屋にぼうっとディスプレイの光に照らされたおっさんの顔がたくさんあるし、デスクの周りにおいてあるのは美少女なフィギュア。一方企画側はオフィス内で弓矢を飛ばして遊んでいた。

欧米は欧米はっていう人たちもいるらしいけど、日本は最高の国ですわ。 泊まってたのはヒルトンホテルだったんだけど、数ブロック離れるとあら不思議マリファナの匂い。というかもう道がめちゃくちゃ汚い。

日本サイコー。

まじで。

Clojure:binding と遅延シーケンス

Clojure in Action読んでて、へえってなったのでメモ。

当然と言えば当然なのかもしれないけれど。

(def ^:dynamic *factor* 10)

(defn multiply [x]
  (* x *factor*))

(map multiply [1 2 3])

(binding [*factor* 20]
  (map multiply [1 2 3]))

Clojure の var はbindingで束縛することができる。

しかし直感に反して上のどちらのmapも [10 20 30] を返す。

これはなぜかというと、mapのような関数は遅延シーケンスを返すからで、この遅延シーケンスは必要となるまで評価されない。そして評価が実際に行われたときはすでにbindingフォームの外であるので、 factor も元々の10に戻っている。

これを意図したようにするには

(binding [*factor* 20]
  (doall (map multiply [1 2 3])))

とする必要がある。

どうてきすこーぷ、気をつけよう。

なんかこれ同じようなことをDBでもやったような気がする。

DBのコネクションをラップしたマクロが用意されてて、そいつが返すのは遅延シーケンスだということに気づかず、コネクションを閉じる前に結果を現実化しなかったせいでDBから結果が返ってきてないように見えて慌てたような。

Clojure in Action

Clojure in Action

夏休み

VOYAGE GROUPとDeNAインターンに参加した。

どちらも徹夜続きで死ぬかと思った。楽しかったけれど。

両方優勝することができたが、他チームと比べてダントツ!ってわけじゃなかったし、悔しい。

DeNAインターンの途中に楽天ハッカソンに参加して惨敗してきた。

相方が担当したフロントエンドは完璧な出来だったのに、僕が担当したバックエンドは本番環境に移したらMeCabがsegmentation fault起こして解決できなかった+APIも間に合わなかった。

ので、フロントエンドとバックエンドは互いにスタンダロンな関係(つながらなかった)のまま評価を受けるはめになった。

凹む。

パーサの連接とか

前回までに出てきた基本的なパーサをくっつけることで複雑なパーサが書けるわけですが、 今回はそのような連接とかを手助けしてくれるパーサコンビネータを紹介?します。

Kern のパーサコンビネータはシーケンス化、反復、選択、それと括弧の対応とかの面倒を見てくれるみたいです。

例のごとく kern をロード。

(use 'blancas.kern.core)

skip-ws は入力のはじめにあるすべての空白をスキップ。

doc.core> (run (skip-ws letter) "\t \n x")
\x
nil

<|> はあたえられたパーサを成功するまで試す。すべてのパーサが失敗するか、入力を消費するパーサが失敗すると全体として失敗する。

doc.core> (run (<|> digit letter) "x")
\x
nil
doc.core> (run (<|> digit letter) "9")
\9
nil
doc.core> (run (<|> digit letter tab) "?")
line 1 column 1
unexpected \?
expecting digit, letter or tab
nil

で、 >> は1つ以上のパーサを適用する。最後のパーサの結果だけを返す。

doc.core> (run (>> digit digit letter) "91x")
\x
nil

<< は1つ以上のパーサを適用する。最初のパーサの結果だけを返す。

doc.core> (run (<< upper (sym* \-) digit digit) "F-16")
\F
nil

<$> は与えられたパーサを入力に適用して、そのあとパースした結果に対して与えられた関数を適用、その結果を返す。

doc.core> (run (<$> #(* % %) dec-num) "12")
144
nil

<*> は2つ以上のパーサを適用して、それらの結果をベクタにして返す。

doc.core> (run (<*> dec-num tab oct-num tab hex-num) "737\t747\tA340")
[737 \tab 487 \tab 41792]
nil

<:> はバックトラックするパーサらしい。もし失敗しても消費した入力は回復。

doc.core> (:input (parse (<:> float-num) "5005.a"))
(\5 \0 \0 \5 \. \a)

many は入力に対しパーサを0回以上、失敗するまで適用。many1 は1回以上で、最低1回生功しないと失敗。

doc.core> (run (many letter) "123")
[]
nil
doc.core> (run (many letter) "xyz")
[\x \y \z]
nil
doc.core> (run (many1 letter) "xyz")
[\x \y \z]
nil
doc.core> (run (many1 letter) "123")
line 1 column 1
unexpected \1
expecting letter
nil

optional はパーサが成功するか消費を行わかなった場合に全体として成功する。

doc.core> (run (<+> (optional (sym* \-)) dec-num) "-45")
"-45"
nil
doc.core> (run (<+> (optional (sym* \-)) dec-num) "45") 
"45"
nil
doc.core> (run (<+> (optional float-num) letter) "45.A")
line 1 column 4
unexpected \A
expecting digit
nil

option は与えられたパーサを適用する。もし入力を消費せずに失敗したら、与えられた値を結果として返すっぽい。

doc.core> (run (option 3.1416 float-num) "foo")
3.1416
nil

skip は1つ以上のパーサを取ってそれらの結果をすべて飛ばす。結果として nil を返す。

doc.core> (run (skip letter digit letter digit) "A5E8") 
nil
nil

skip-many はパーサを0回以上適用し結果を飛ばす。nil を返す。skip-many は1回以上。

doc.core> (run (skip-many letter) "xyz")
nil
nil
doc.core> (run (skip-many1 letter) "xyz") 
nil
nil
doc.core> (run (skip-many1 letter) "123")
line 1 column 1
unexpected \1
expecting letter
nil

sep-by は与えられたパーサによって分けたものに対して別のパーサを0回以上適用する。sep-by1 は1回以上。

doc.core> (run (sep-by (sym* \&) (many1 letter)) "a&bb&ccc&ddd")
[[\a] [\b \b] [\c \c \c] [\d \d \d]]
nil

end-by は与えられたパーサによって別れる/終わるものに対して別のパーサを0回以上適用する。end-by1 は1回以上。

doc.core> (run (end-by (sym* \&) (many1 letter)) "a&bb&ccc&")
[[\a] [\b \b] [\c \c \c]]
nil

between は1,2つ目のパーサの間のちに対して3つ目のパーサを適用する。

doc.core> (run (between (sym \() (sym \)) dec-num) "(1984)")
1984
nil

<+> はひとつ以上のパーサをとる。で、結果を潰してくっつけて、文字列にする。

doc.core> (run (<+> letter (many alpha-num)) "a177")
"a177"
nil

数字のパース

kern にはいくつか数値のパーサが用意されてて、これらは結果として文字列ではなく適切な数値型を返します。これらのパーサは名前空間 lexer 内のそれらに比べてサポートする数値型は少ないし、空白とかコメントとかもスキップしません。つまるところ、大きなパーサの低レベルの部分で使うべき関数みたいですね。

使用にあたって Kern の名前空間 core をロードします。

(use 'blancas.kern.core)

value はパーサを走らせてそのパース結果を返す関数です。引数としてパーサと文字列を受け取って、オプションとして文字列が何であるか(例えばファイルネームだとか)を示すことができます。

dec-num は十進数の正数をパースして結果として long 型を返します。

doc.core> (value dec-num "8086")
8086

oct-num は8進数の正数をパースして結果として long 型を返します。

doc.core> (value hex-num "747")
487

hex-num は16進数をパースして結果として long 型を返します。 0x が先頭につくものは許されません。

doc.core> (value hex-num "FEED")
65261

float-num は浮動小数点数をパースして結果として double 型を返します。指数表現はサポートされていません。 lexer 内の float-lit ではサポートされてます。

doc.core> (value float-num "3.1415927")
3.1415927

Kern のプリミティブパーサ

Kern にもプリミティブなパーサがあって、これらを組み合わせていくことで大きなパーサを作るというのが関数型らしいパーサの作り方なわけですが。 それらをちょっと見ていきます。 Kern けっこうかわいいわあ。

(use 'blancas.kern.core)

return は常に成功し、与えられた値を返す。

doc.core> (run (return "foo") "xyz")
"foo"
nil

fail は与えられたエラーメッセージとともに失敗する。

doc.core> (:ok (parse (fail "foo") "xyz"))
false
doc.core> (run (fail "this is bad") "xyz")
line 1 column 1
this is bad
nil

satisfy は与えられた述語を入力の最初の文字に適用し、それによって成功か失敗かが決まる。

doc.core> (run (satisfy #(= \x %)) "xyz")
\x
nil

any-char は入力された文字を返す。

doc.core> (run any-char "x")
\x
nil

letter は大文字小文字のどちらも成功する。

doc.core> (run letter "z")
\z
nil

lower, upper はそのまんま。

doc.core> (run lower "a")
\a
nil
doc.core> (run upper "M")
\M
nil

white-space は空白文字を受け付ける。

doc.core> (run white-space "\t")
\tab
nil

space はスペースだけ。 tab もまあそんなかんじ。

doc.core> (run space " ")
\space
nil

digit は \0 から \9 までの数字を受け付ける。 hex-digit はそれに加えて \A から \F まで。 oct-digit は \0 から \7 まで。

doc.core> (run digit "9")
\9
nil

alpha-num は英数字。

doc.core> (run (many alpha-num) "9A")
[\9 \A]
nil

sym* は特定の文字だけ。 sym- は大文字小文字関係なく。ただし結果として得る文字は sym- に渡された文字。

doc.core> (run (sym* \x) "x")
\x
nil

token* は Haskell でいう string パーサ。複数のシーケンスが渡された場合、どれかが成功するまで試され続ける。 token- は大文字小文字関係なく。

doc.core> (run (token* "foo" "bar" "baz") "bazaar")
"baz"
nil

word は token と同じようなものだけれど、許可しない終端を設定できる。 word- は大文字小文字関係なく。

doc.core> (run (word* (one-of* "|-/") "foobar"))
"foobar"
nil
doc.core> (run (word* (one-of* "|-/") "football" "foobar") "foobar*")
"foobar"
nil
doc.core> (run (word* (one-of* "|-/") "foobar") "foobar/")
line 1 column 8
unexpected /
expecting end of foobar
nil

one-of は与えられた文字列のどれかの文字であれば受け付ける。 none-of はその逆。

doc.core> (run (one-of* "xyz") "zap")
\z
nil

new-line* は \n を受け付ける。 eof はもし入力が空なら成功する。

field* は与えられた文字列で区切られる文字列をパースする。

doc.core> (run (field ",;") "California,")
"California"
nil

split-on は空白か与えられた文字列で区切られる文字列を分割する。

doc.core> (run (split-on ",./") "Now, is the time")
["Now" "is the time"]
nil

split は空白で区切られたテキストを分割する。

doc.core> (run split "Now is the time")
["Now" "is" "the" "time"]