K2NR.ME

このエントリーをはてなブックマークに追加 Tweet

clojure1.7のtransducersの中身を見てみる

続き

もう少しtransducersの中身を見ていきます。

まず、前に取り上げたmapですが、実際のコードはこうなってます。 (transducerの部分のみ抜粋)

  ([f]
    (fn [f1]
      (fn
        ([] (f1))
        ([result] (f1 result))
        ([result input]
           (f1 result (f input)))
        ([result input & inputs]
           (f1 result (apply f input inputs))))))

mapは、関数を返す関数を返す関数です。(mapにかぎらず、transducerを返す関数はもちろんそうです) fmapの第一引数、mapping functionです。で、f1がtransducerの入力となるreducing functionです。transducerはreducing functionを入力に受けて、別のreducing functionを返す関数なので、f1はこの入力にあたります。

一番大事っぽい部分は2引数を受け取る

([result input]
           (f1 result (f input)))

の部分ですね。元のreducing functionf1resultと、inputをmapping functionfで変換した値を渡しています。

これがmapping transducerです。

ではもう一つ、dropを見てみます。

dropは従来は

(drop 1 [1 2 3])
; => (2 3)

第一引数の数だけ第二引数のシーケンスの先頭から取り除いたシーケンスを返す関数なのですが、第二引数が省略されるとtransducerを返します。dropping transducerと呼ぶのでしょうか。

(def d (drop 1))

(sequence d [1 2 3])
; => (2 3)

コードはこんな感じ

  ([n]
     (fn [f1]
       (let [na (atom n)]
         (fn
           ([] (f1))
           ([result] (f1 result))
           ([result input]
              (let [n @na]
                (swap! na dec)
                (if (pos? n)
                  result
                  (f1 result input))))))))

これはちょっとおもしろいですね。 dropの第一引数をatomnaにとっておいて、dropping transducerによって作られるreducing functionは呼ばれる度にnaを-1していきます。naが正数の間はreducing functionf1を適用せずにresultをそのまま返すことで、dropの動きを実現しています。

drop以外にもtake-nthとか、長さを変える系のtransducerはこういう仕組みになってます。

transducerを受け取ってtransduceする側の関数も紹介しようかと思ったけどここで力尽きた

comments powered by Disqus