日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

プログラミング Clojure 6.6 スネークゲーム を seesaw で作る(2)

昨日の続き。

GUI 部分もう少し触ろうかと思ったけど、Model 部分が思ったより理解できて
なかったのでそちらから。
Lancet の部分読み飛ばしまくってるせいかなあ。

関数モデルの部分何か難しくないすか?

vector のインデックス指定

まずは point-to-screen-rect。

 (defn point-to-screen-rect [pt]
   (map #(* point-size %)
        [(pt 0) (pt 1) 1 1]))

(pt 0) (pt 1)って・・・vector だとこんな index 指定で中身取り出せるんかい。
と言うかこんな説明前にあったっけか。

じゃ他のシーケンスはどうなんと試してみる

 user> ([:a :b :c] 2)              ; vector
 :c
 user> (#{:a :b :c} 2)             ; set
 nil
 user> ({:a "a" :b "b" :c "c"} 2)  ; map
 nil
 user> ((list 1 2 3) 1)            ; list
 ClassCastException clojure.lang.PersistentList cannot be cast to clojure.lang.IFn  user/eval13330 (NO_SOURCE_FILE:1)

ふーん、index 指定で取れるのは vector だけなのね。
そういやシーケンスに含まれる個別データ型特有の操作については読み飛ばし
てたような気がするw

map の引数詳細指定その 1

次の move にまた驚く。なんじゃこの引数指定は。

 (defn move [{:keys [body dir] :as snake} & grow]
   (assoc snake :body (cons (add-points (first body) dir)
                            (if grow body (butlast body)))))

正直引数の渡し方は最初理解不能だったw
本だと少し説明不足な気がするので(自分が読み飛ばしてる可能性も大いにあるけど)
自分なりに理解するためにもどういう動きなのかを書いてみる。

{:keys [body dir] :as snake}

やってそうな処理を整理すると、

  1. {:keys [body dir] :as snake} と言う引数指定された
  2. {} で囲まれてるから map だね
  3. :keys 指定があるとその後の vector を見るよ
    1. vector に body, dir とあるね、じゃ map の中に :body :dir があるか探すよ
    2. map の :body, :dir で取れた値を関数内引数の body dir として束縛するよ
  4. あ、:as もあるね、じゃ関数内では map 全体を snake って名前で束縛するよ

こんな感じ?

map の引数詳細指定その 2

win? もさらりと 2 行で書いてるんだがここもまた map での引数指定方法が微妙に違ったり。

(defn win? [{body :body}]
  (>= (count body) win-length))

まあ多分 map の中の :body キーワードで取れる key と val で構成された
map を body に束縛してるんだろうってのはこの後のテストの仕方で何となく分かるが。

map の引数詳細指定その 3

はい次々と良く分かりません。

 (defn lose? [{[head & body] :body}]
   (if (some #(= head %) body) true false))

includes? 持ってくるのめんどくさくて書き直してるがそこは重要じゃなくて
話したいのは引数の指定の方法。

  1. {[head & body] :body} が来た
  2. map 中で :body を探すのはまあさっきと一緒
  3. おっと関数内引数が [] で囲まれてるな、じゃ値だけ渡すよ
  4. ん、head & body となってるのか、じゃ先頭要素を head にして残りは body に分けますね

何か書いててヘンなテンションになってきた。

eats?

ここも地味に引数の指定の仕方が違うのに注意。

 (defn eats? [{[snake-head] :body} {apple :location}]
   (= snake-head apple))

snake の body は体の長さの分だけ位置要素を持つので{snake-head :body}
とか [] で囲むのを忘れると body 全体を取ってくるのに対してapple の
location は x,y 位置の 2 要素を持つだけの vector なので [
] では囲んで
ない。

まとめ

動作的には多分自分が考えてるので辻褄が合うと思ってるんだができれば答え合わせしたい。
詳しい Clojurian の人この辺りの仕様ってどこに書いてあるんですかね。

追記:
公式のここに書いてた。
なるほどねえ。

今日中目標にしてたけど怪しくなってきた。。。