transientとpersistentについて

Clojureの1.1から使えるようになった機能。Clojureの公式サイトから引用すると

If a tree falls in the woods, does it make a sound?
If a pure function mutates some local data in order to produce an immutable return value, is that ok?

Clojure - Transient Data Structures

最初のやつはなんと訳していいのかよく分からんけど、2つ目のやつは「(関数型言語は同じ入力を入れたら同じ出力を返して欲しいわけだけど)、immutableな出力を得るのに、localなデータだったら変更したりしてもいいんじゃね?(パフォーマンスとかを求めて)」という感じのニュアンスだと思う、たぶん。

コードを見たほうがたぶん早い。Clojureの公式サイトのほうは空配列からconjしていく例で書いてあるけど、本当に変更されてないのかetcを見る場合にはよく分からんので下のような例を書いてみた。

(def x '[0 1 2 3 4])
(let [tmp (transient x)]
  (persistent! (assoc! tmp 0 100))) ; [100 1 2 3 4]
x ; [0 1 2 3 4]

transientしてやったオブジェクトはassoc!やconj!のような関数をかましてやると上のようにできる。最後はpersistent!を使って状態不変なClojureの世界に逃げ込ませる。元々のオブジェクトは変わっていないことに注意。

The Joy of Clojureのchapter 12にも書いてあるが、ちっこいvectorとかを変更しまくる場合には返って遅くなる場合がある。大きいvectorとかをいじる場合には早くなる場合がある。そこに書いてある"THE RULE OF TRANSIENTS"によると

Write your code so that it's first and foremost correct using immutable collections and operations; then, make changes to use transients for gaining speed.

だそうです。

The Joy of Clojure

The Joy of Clojure