ClojureとQuilでいろいろ降らせてみた
前回の元記事の作者の方がこのような記事をQiitaに挙げていた:
前回のコードは基本的に形を座標リストとして表してしまえばあとは同じ関数でデータ作成・描画・アップデートができるので、とりあえずいろいろ降らせてみた。
とりあえず円、正多角形、星、ハート、楓を作ってみた。
円は簡単:
(defn circle-fn [t] (let [radt (q/radians t) x (/ (q/cos radt) 3) y (/ (q/sin radt) 3)] [x y])) (def circle-shape (map circle-fn (range 360)))
(cos n, sin n)
だからね・・・
多角形は高階関数になっている:
(defn polygon-fn [v] (fn [t] (let [z (-> t (/ v) (* 360) q/radians) x (/ (q/cos z) 3) y (/ (q/sin z) 3)] [x y]))) (def triangle-shape (map (polygon-fn 3) (range 3))) (def hexagon-shape (map (polygon-fn 6) (range 6)))
上の円と同じ理屈で、描く点の数を減らしているのがポイント。ただ、私の実装だと同じ数をpolygon-fn
とrange
の二つに一回ずつ渡しているのがダサい・・・
星も同じ理屈:
(defn star-fn [v] (fn [t] (let [r (inc (mod t 2)) z (-> t (/ v) (* 360) q/radians) x (-> z q/cos (* r) (/ 3)) y (-> z q/sin (* r) (/ 3))] [x y]))) (def five-star-shape (map (star-fn 10) (range 10)))
五芒星の場合、10点の頂点があり、そのうちの5つが外側の円、5つが内側の円の上に乗っている。
ハート:
(defn heart-fn [t] (let [radt (q/radians t) R 1/50 x (* R 13 (q/sin radt) (q/sin radt) (q/sin radt)) y (* -1 R (- (-> t q/radians q/cos (* 13)) (-> t (* 2) q/radians q/cos (* 5)) (-> t (* 3) q/radians q/cos (* 2)) (-> t (* 4) q/radians q/cos)))] [x y])) (def heart-shape (map heart-fn (range 360)))
楓:
(defn leaf-fn [t] (let [R 1/8 r (* -1 (-> t (* 8) q/radians q/cos (* 9) (/ 10) inc) (-> t (* 24) q/radians q/cos (/ 10) inc) (-> t (* 200) q/radians q/cos (/ 10) (+ 0.9)) (-> t q/radians q/sin inc)) x (-> t q/radians q/cos (* r R)) y (-> t q/radians q/sin (* r R))] [x y])) (def leaf-shape (map leaf-fn (range 360)))
ここらへんは正直理屈はわからぬ・・・
あとはshapes
という名のvector
に集めて、make-sakura
(今になって思えば関数名を変えるべきだった)の中でランダムにshapes
の中から座標リストを取ってくるようにしてある:
(def shapes [sakura-shape circle-shape triangle-shape hexagon-shape five-star-shape heart-shape leaf-shape]) (defn make-sakura [] (let [...] {:shape (nth shapes (q/floor (q/random (count shapes)))) ...}))
このように簡単に表示・動く形を増やすことができる。
ClojureとQuilで桜を吹雪かせてみた
こういう記事を読んだ:
とても面白そうだったのでひさしぶりにQuilで再現して遊んでみよう、と思ったら意外と色々忘れていてえらく時間がかかった。
とりあえず結果:
Run
ボタンクリックで桜吹雪が見えるはず。
コードは以下の通り。
まずは基本形である花びらの座標を定義:
(defn sakura-fn [t] (let [radt (q/radians t) A (* (/ 4 q/PI) radt) md (mod (q/floor A) 2) r (+ md (* (q/pow -1 md) (- A (q/floor A)))) R (+ r (* 2 (min 0 (- 0.8 r)))) x (* R (q/cos radt)) y (* R (q/sin radt))] [x y])) (def sakura-shape (map sakura-fn (range 90)))
この座標情報の詳細を知りたい方は元記事とそのさらに元であるこの記事を見てほしい:
次にその座標も含めた様々な花びら情報をある程度ランダムに作成する関数:
(defn make-sakura [] (let [xDef (q/random 500) xAmp (q/random 50 100) xTheta (q/random 360) size (q/random 40 100) ] {:shape sakura-shape :color (nth [[244 191 252 150] [255 219 248 150] [246 204 252 150]] (q/floor (q/random 3))) :xDef xDef :xAmp xAmp :xTheta xTheta :size size :ox (+ xDef (* xAmp (q/sin (q/radians xTheta)))) :oy (q/random 500) :rotateT (q/random 360) :xSpeed (q/random 1 2) :ySpeed (/ size 20) :sizeYScale 1 :sizeYT (q/random 360) :sizeYSpeed (/ size 30)}))
その個々の花びら情報を描画する関数。基本形の座標を個々の花びらの情報ですこし変形させてからq/vertex
で描画している:
(defn draw-sakura [sakura] (q/push-matrix) (apply q/fill (:color sakura)) (q/translate (:ox sakura) (:oy sakura)) (q/rotate (:rotateT sakura)) (q/begin-shape) (doseq [[x y] (:shape sakura)] (q/vertex (* x (:size sakura)) (* y (:size sakura) (:sizeYScale sakura)))) (q/end-shape :close) (q/pop-matrix))
1フレームごとに個々の花びらの情報をアップデートする関数:
(defn move-sakura [sakura] (let [oy (+ (:oy sakura) (:ySpeed sakura)) sizeYT (+ (:sizeYT sakura) (:sizeYSpeed sakura))] (-> sakura (assoc :ox (+ (:xDef sakura) (* (:xAmp sakura) (q/sin (q/radians (:xTheta sakura)))))) (update :xTheta #(+ % (:xSpeed sakura))) (assoc :sizeYT sizeYT) (assoc :sizeYScale (-> sizeYT q/radians q/sin q/abs)) (assoc :oy (if (> oy (+ 500 (:size sakura))) (- (:size sakura)) oy)))))
全体的に元記事のコードをClojureに書き写しただけ。ただ、元記事はy += x; z += y; if (z > ...)
的なコードがあるので、そこはClojureのimmutable性とバッティングしないよう気をつけている。
あとはQuilの関数であるsetup
で初期化、update-state
で1フレームごとに全体の情報をアップデート、draw-state
で全体を描画:
(defn setup [] (q/frame-rate 30) (q/color-mode :hsb) {:color 0 :angle 0 :sakura (repeatedly 20 make-sakura)}) (defn update-state [state] (update state :sakura #(map move-sakura %))) (defn draw-state [state] (q/background 240) (doall (map draw-sakura (:sakura state))))
このコード、多分draw-state
にすごく仕事させている気がする。というのはsetup
とupdate-state
だとrepeatedly
やmap
といった遅延評価シーケンスを返す関数しか使っていないので、draw-state
になるまでは情報がアップデートされていない。どうせupdate-state
もdraw-state
も1フレームに一回呼ぶんだしいいかと思っているが、気をつけるなら:
(defn update-state [state] (update state :sakura #(vec (map move-sakura %))))
で無理やり評価してしまう手もある。
Effective C++勉強メモ: Item 21 Pass-by-存在しないオブジェクトへのReferenceしてはいけない・・・
前回の項目を理解するとなんでもPass-by-Referenceしたくなるが、ReferenceはそもそもちゃんとReferenceを返した先でも存在し続けるオブジェクトのものでなければならず、その条件が満たせないことも多い。という項目。
例えばA
クラスにoperator*
を定義して以下のような処理を可能にしたいとする:
A a; A b; A c = a * b;
operator*
がconst A
を返すとすると、a * b
で関数内で一度作成されたA
インスタンスを関数から返す時にまたコピーする必要性が生じる。一旦作ったものがあるのだからそれを返せばいい、と:
class A { public: const A& operator* (const A& lhs, const A& rhs); };
のような実装をしてしまいがちだが、その場合関数内で作ったA
インスタンスはstackかheapのどちらかにいることになる。
Stackの場合
関数内でstackに乗せたインスタンスは関数から出る時に回収されるので、Pass-by-Referenceした途端にReferenceされているオブジェクトが消失する。のでダメ。
Heapの場合
const A& operator=(const A& lhs, const A& rhs) { A* result = new A(lhs.n * rhs.n); return *result; }
などとやった場合、たしかにオブジェクトは永続するが、そのおかげで受け取り側がちゃんとdelete
しないとメモリリークになる。
そしてA d = a * b * c;
などのコードの場合、そもそもdelete
できないので絶対にメモリリークになる。
関数内でStaticオブジェクトを保持する
ならそもそも新しいオブジェクトを作成するのではなく、staticなインスタンスを関数の中で使い回せば・・・ と(正直無理筋っぽいことを)考えたなら、それもダメである。
if (a*b == c*d) {...}
が必ずTrueになってしまう。同じインスタンスへのreferenceを比較してるんだからそれはそうだよね。
こういうケースはおとなしくPass-by-Valueしよう。
Thinking Functionally with Haskell勉強メモ: 第4章問題1
Exercise A
null :: [a] -> Bool null [] = True null _ = False
という定義を
null = (==[])
と変えても大丈夫か?という問題。
実際使ってみると大丈夫そうなのだが:
Prelude> null = (==[]) Prelude> null [] True Prelude> null [1] False Prelude> null [True] False Prelude> null [False] False
念のため、型を調べてみる:
Prelude> :t null null :: Eq t => [t] -> Bool
Eq t
という条件が入ってきているのがわかる。つまりリストの要素に「等号で比較可能」という型クラスの指定が入っている。
Eq
のインスタンスではない型を作成してみてnull
にその型の要素を持つリストを渡してみる:
Prelude> data Greek = Alpha | Beta Prelude> null [Alpha] <interactive>:8:1: error: • No instance for (Eq Greek) arising from a use of ‘null’ • In the expression: null [Alpha] In an equation for ‘it’: it = null [Alpha]
これで問題点がはっきりする。リストに対する(==)
は多分処理が「すべての要素が一致する」というものなので、要素自体が等号で比較できなければいけない。
パターンマッチなら大丈夫:
Prelude> :{ Prelude| null :: [a] -> Bool Prelude| null [] = True Prelude| null _ = False Prelude| :} Prelude> null [Alpha] False
というわけで(==[])
には(かなりわかりにくい形で)型クラスの制限が紛れ込んでしまう、というのが問題。
Exercise B
[(0,0), (1, 0), (0, 1) ...]
など、任意のnとmで構成される(n, m)
が有限o個目の要素として出てくるリストを作成する、という問題。
allPairs = [(x, y) | x <- [0..], y <- [0..]]
という問題文に登場する実装だと、[(0,1), (0,2), (0,3) ... (0, n) ...]
と有限個目の要素はすべて(0, m)
の形になる。
oneSidedPairs = [(x, y) | x <- [0..], y <- [0..x]] mirrorPairs :: (Eq a) => [(a, a)] -> [(a, a)] mirrorPairs [] = [] mirrorPairs ((x, y):xys) | x == y = (x, y) : mirrorPairs xys | otherwise = (x, y) : (y, x) : mirrorPairs xys allPairs = mirrorPairs oneSidedPairs
というふうに、yの上限をxの値までにすればいい。
Exercise C
二つのソートされたリストに同じ要素が含まれているか判定する関数disjoint
を定義せよ:
disjoint :: (Ord a) => [a] -> [a] -> Bool disjoint [] _ = True disjoint _ [] = True disjoint xs'@(x:xs) ys'@(y:ys) | x == y = False | x < y = disjoint xs ys' | otherwise = disjoint xs' ys
Exercise D
以下の二つのリストが同一である条件は:
[e | x <- xs, p x, y <- ys] [e | x <- xs, y <- ys, p x]
実際には結果が同一でない条件の方が厳しい。
[e | x <- [0..], y <- (if x == 5 then [0..] else [0..5]), (>5) x]
といった場合、つまりp x
が成り立たない場合にys
が無限になるケースがあり、その後に来る結果を隠してしまう状況だと不一致になる。
結果は同一になりやすいが、計算時間は大きく違ってくる。前者はp x
が成り立たない場合そもそもy <- ys
が抜けるので、そのループ部分は評価されない。後者は二重ループを評価してからフィルタしているようなイメージ。
Exercise E
a^3+b^3=c^3+d^3=x
でa,b
がc,d
とは違うという条件で最小のx
は1729だという事実に関連したラマヌジャンの逸話がある。他のx
の値を小さい順に算出せよ。
cubeSums = [ (x, a, b, c, d) | x <- [1..], a <- (takeWhile (\a -> a^3 < x) [1..]), b <- (takeWhile (\b -> a^3 + b^3 <= x) [a..]), a^3 + b^3 == x, c <- (takeWhile (\c -> c^3 < x) [a+1..]), d <- (takeWhile (\d -> c^3 + d^3 <= x) [c..]), c^3 + d^3 == x]
これで結果が
Prelude> take 5 cubeSums [(1729,1,12,9,10),(4104,2,16,9,15),(13832,2,24,18,20),(20683,10,27,19,24),(32832,4,32,18,30)]
となる。時間はかかるが・・・
本日のvim道 2018.3.14
今日はウィンドウの分割関連。知ってるといえば知ってるけど指に染み込んでないコマンド群。未だにtmuxの画面分割とどっちを使うべきか悩むが・・・
C-w [sv]
でウィンドウをパネル分割
とりあえずウィンドウ分割関連はC-w
で始まる。
C-w s
で上下に(横長に)分割。C-w v
で左右に(縦長に)分割。
デフォルトでは現在開いているファイルと同じものが新しい分割パネルにも表示される。あるいは:split [filename]
/:vsplit [filename]
でファイル指定も可能。
C-w C-w
で次のパネルに移動、C-w [hjkl]
で方向指定
ウィンドウのパネルからパネルへと移動するのにはC-w C-w
(実際にはC-w w
も可能だがC-w
連打のほうが楽)。あるいは上下左右におなじみのvimコマンドのhjkl
を指定することも。
C-w [=_|]
でパネルサイズ変更
C-w =
ですべてのパネルを同じ大きさに、ということだが上下左右に不均一に分割している婆、均等にできない場合もある。そういう場合の挙動もまあまあリーズナブル。C-w |
で横方向に最大化、C-w _
で縦方向に最大化。個人的にはちょっと直感的じゃない気がしてしまうが・・・
C-w [co]
でパネルを閉じる
今いるパネルをC-w c
で閉じる。今いるパネルを残して他を閉じるのはC-w o
。
Thinking Functionally with Haskell勉強メモ: 第4章3 zipWithとmerge sortの実装
zipWith
ふたつのリストから一要素ずつ取って関数を適用するzipWith
関数:
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys zipWith f _ _ = []
この定義はちょっと面白い。再帰を書く場合、いつも停止条件を最初に書く作法でやっているのだが、停止条件が最後にきて、しかもdon't care記法なので、「最初の条件にマッチしなければ再帰終了」というロジックが非常に簡潔に記述できる。
zipWith
は便利で、例えばリストが小さい順に並んでいるか判定するnondec
関数をこのように表記できる:
nondec :: (Ord a) => [a] -> Bool nondec xs = and (zipWith (<=) xs (tail xs))
これはPythonでも
def nondec(xs): return all(x < y for x, y in zip(xs, xs[1:]))
と書きたくなる。
Merge sort実装
sort :: (Ord a) => [a] -> [a] sort [] = [] sort [x] = [x] sort xs = merge (sort ys) (sort zs) where (ys, zs) = halve xs halve :: [a] -> ([a], [a]) halve xs = (take n xs, drop n xs) where n = length xs `div` 2 merge :: (Ord a) => [a] -> [a] -> [a] merge [] xs = xs merge xs [] = xs merge xs'@(x:xs) ys'@(y:ys) | x <= y = x : merge xs ys' | otherwise = y : merge xs' ys
これはすごく綺麗だ。まさに宣言的なコード、という感じがする。
Effective C++勉強メモ: Item 20 Pass-by-Const-Reference
Pass-by-valueとpass-by-const-referenceのどちらか迷った時は大抵pass-by-const-referenceを選ぼう、という項目。
void printName(MyObj o) { std::cout << o.name(); } void printName(const MyObj& o) { std::cout << o.name(); }
前者がpass-by-value、後者がpass-by-const-reference。
pass-by-valueの場合、引数となるオブジェクト自身のメンバデータに加え、メンバデータがポインタだった場合などは大抵そのポインタ先のデータもすべてコピーされ、そして関数から出る時に破棄される。その代わり関数の中でconst
ではないメンバ関数や処理を使っても引数の元のオブジェクトは変わらない。
しかし、もし関数の中の処理が引数のオブジェクトに対してはconst
なものしか使わない場合、pass-by-const-referenceすることでいちいちコピー、破棄するコストを避けることができる。(個人的にはうっかりconst
じゃない処理を書いてしまった場合はcompiler errorなのも嬉しい)
さらに、C++がpass-by-base-class-valueして作成されるコピーには、継承先のクラスのデータが欠如してしまうという特徴がある:
class A { public: int a, b, c; virtual const std::string& name(); } class X : public A { public: std::string m_name; const std::string& name() { return m_name; } } void f(A o) { std::cout << o.name(); } X x(); f(x);
このコードはエラー。何故ならf関数の中でオブジェクトoはAのメンバデータa、b、cは持つがXのデータであるm_nameはコピーされず、name関数が存在しないm_nameを返そうとするから。ポインタなら問題ないが、この場合はnameがconst std::string&を返すのでfをpass-by-constにしても問題なくnameが使える。
Pass-by-const-reference非推奨なのはコピーが絶対に安いデータ。built-in型とstlのイテレータ、関数オブジェクトがそれだ。これらはpass-by-const-referenceよりもpass-by-valueの方が安い(const-referenceを作成するよりデータを素直にコピーしてしまう方がコストが低くて済む)。
それ以外は全部なるべくpass-by-const-reference。