lazy-seqでmapを書いてみる?
「?」が付いていることに注意していただきたい。
実際に書いてみるまでは、正直なめていた。
というのも、まずは(map inc (range 10))
というような書き方・使い方を想定していたのだ。
一つの関数と一つのシーケンスを引数として、シーケンスに含まれる値に関数を適用した値の連なりを返す、というもの。(map inc (range 10))
だとしたら結果は1〜10までのリスト。
しかし、よく考えてみると、そもそも私自身、今まで何回も(map vector [1 2 3] [4 5 6])
のような使い方もしてきた。ちなみにこの式の結果は([1 4] [2 5] [3 6])
であり、Pythonのzip
関数と同じ挙動になる。
つまり、複数のシーケンスを引数として受け取れるのだ。それを踏まえてmap
を書くとしたら以下のような感じか。
(defn my-map [f col & other-cols] (let [cols (con col other-cols)] (if (some empty? cols) () (lazy-seq (cons (apply f (for [c cols] (first c))) (apply my-map f (for [c cols] (rest c))))))))
とりあえず関数一つと、一つ以上のシーケンスが引数でリストを返す。各シーケンスから第一要素を取り出すために(for [c cols] (first c))
、そして第二要素以降のリストを次のmy-map
に回すために(for [c cols] (rest c))
を使っている。
まあこんなものかな?と思って定義を読んでみる。
Returns a lazy sequence consisting of the result of applying f to the set of first items of each coll, followed by applying f to the set of second items in each coll, until any one of the calls is exhausted. Any remaining items in other colls are ignored. Function f should accept number-of-colls arguments. Returns a transducer when no collection is provided.
最後のはなんだ?
ググってみる。
Transducers are Coming — Cognitect Blog
Clojure - Clojure 1.7 is now available
Some trivial examples of using Clojure Transducers - An Ostler in IT
・・・transducer?知らん。(すっとぼけ
とは言わないまでも、一旦見なかったことにして置いておこうと思う。
軽く読んだ限りだと(map inc)
という式自体が関数を返して、それをcomp
だとかで組み合わせてtransduce
関数でコレクションに対して適用して使うっぽいが・・・ どうやって再現すればいいのかなんて現段階では見当もつかない。これを触るのはもう少し功夫を積んでからでいいだろう。
エントリの題に「?」が付いている所以である。