transducer가 어떤 방식으로 작동하는지 알기 위해 테스트 코드를 작성했습니다. 아래의 my-filter
, my-map
, my-conj
는 각각 clojure.core
의 filter
, map
, conj
함수의 내용 일부를 이 테스트를 위해 약간 수정한 것입니다. 코드가 약간 길기는 합니다만, 찬찬히 읽어 보면 이해하기 어려운 코드는 아닙니다.
Tip
|
참고로, 아래에서 outer-fn-in- 으로 시작하는 함수는 transducer이고,
inner-fn-in- 으로 시작하는 함수는 reducing function입니다. Clojure에서는 무명 함수에도
이와같이 이름을 붙일 수 있습니다. 아래처럼 디버깅할 때 요긴합니다.
|
(ns clj-test.transducer)
(defn my-filter
[pred]
(fn outer-fn-in-filter [rf]
(fn inner-fn-in-filter
([]
(let [r (rf)]
(println "rf-in-filter =" rf)
(println "my-filter [] post: result =" r)
r))
([result]
(println "rf-in-filter = " rf)
(println "my-filter [result] pre: result =" result)
(let [r (rf result)]
(println "my-filter [result] post: result =" r)
r))
([result input]
(println "rf-in-filter = " rf)
(println "my-filter [result input] pre: result =" result ", input =" input)
(let [r (if (pred input)
(rf result input)
result)]
(println "my-filter [result input] post: result =" r)
r)))))
(defn my-map
[f]
(fn outer-fn-in-map [rf]
(fn inner-fn-in-map
([]
(println "rf-in-map =" rf)
(let [r (rf)]
(println "my-map [] post: result =" r)
r))
([result]
(println "rf-in-map = " rf)
(println "my-map [result] pre: result =" result)
(let [r (rf result)]
(println "my-map [result] post: result =" r)
r))
([result input]
(println "rf-in-map = " rf)
(println "my-map [result input] pre: result =" result ", input =" input)
(let [r (rf result (f input))]
(println "my-map [result input] post: result =" r)
r)))))
(defn my-conj
([]
(println "my-conj []: result =" []) ;; (1)
[])
([result]
(println "my-conj [result]: result =" result)
result)
([result input]
(println "my-conj [result input] pre: result =" result ", input =" input)
(let [r (. clojure.lang.RT (conj result input))]
(println "my-conj [result input] post: retrun =" r)
r) ))
(def xform (comp (my-filter odd?) (my-map #(* % 10))))
(transduce xform my-conj [1 2 3 4 5])
다음은 위 함수를 실행한 결과입니다. 화면 출력 결과는 보기 좋도록 일부 편집했습니다.
;;;; transduce의 함수 인자에 초기값이 지정되어 있지 않아, 위의 (1) 부분이 실행되었다.
; my-conj []: result = []
;;;; <<<< 이 부분이 가장 중요 >>>>
;;;; 아래의 rf-in-filter 함수는 최종적으로 inner-fn-in-map 함수를 호출하므로,
;;;; transducer가 아니라 reducing function이다. 다시 말해, comp 함수의 함수 합성을
;;;; 통해 받게 되는 인자 rf는 transducer가 아니라 reducing function이다.
; rf-in-filter = #function[clj-test.transducer/my-map/outer-fn-in-map--6819/inner-fn-in-map--6820]
; my-filter [result input] pre: result = [] , input = 1
; rf-in-map = #function[clj-test.transducer/my-conj]
; my-map [result input] pre: result = [] , input = 1
; my-conj [result input] pre: result = [] , input = 10
; my-conj [result input] post: retrun = [10]
; my-map [result input] post: result = [10]
; my-filter [result input] post: result = [10]
; rf-in-filter = #function[clj-test.transducer/my-map/outer-fn-in-map--6819/inner-fn-in-map--6820]
; my-filter [result input] pre: result = [10] , input = 2
; my-filter [result input] post: result = [10]
; rf-in-filter = #function[clj-test.transducer/my-map/outer-fn-in-map--6819/inner-fn-in-map--6820]
; my-filter [result input] pre: result = [10] , input = 3
; rf-in-map = #function[clj-test.transducer/my-conj]
; my-map [result input] pre: result = [10] , input = 3
; my-conj [result input] pre: result = [10] , input = 30
; my-conj [result input] post: retrun = [10 30]
; my-map [result input] post: result = [10 30]
; my-filter [result input] post: result = [10 30]
; rf-in-filter = #function[clj-test.transducer/my-map/outer-fn-in-map--6819/inner-fn-in-map--6820]
; my-filter [result input] pre: result = [10 30] , input = 4
; my-filter [result input] post: result = [10 30]
; rf-in-filter = #function[clj-test.transducer/my-map/outer-fn-in-map--6819/inner-fn-in-map--6820]
; my-filter [result input] pre: result = [10 30] , input = 5
; rf-in-map = #function[clj-test.transducer/my-conj]
; my-map [result input] pre: result = [10 30] , input = 5
; my-conj [result input] pre: result = [10 30] , input = 50
; my-conj [result input] post: retrun = [10 30 50]
; my-map [result input] post: result = [10 30 50]
; my-filter [result input] post: result = [10 30 50]
;;;; 아래의 실행 결과에서 최종 결과값을 반환할 때, reducing funcion에 제공된 함수들 중에서,
;;;; 인자 하나짜리 함수를 호출하는 것을 확인할 수 있다.
; rf-in-filter = #function[clj-test.transducer/my-map/outer-fn-in-map--6819/inner-fn-in-map--6820]
; my-filter [result] pre: result = [10 30 50]
; rf-in-map = #function[clj-test.transducer/my-conj]
; my-map [result] pre: result = [10 30 50]
; my-conj [result]: result = [10 30 50]
; my-map [result] post: result = [10 30 50]
; my-filter [result] post: result = [10 30 50]
; => [10 30 50]
위의 예제를 실행해 본 결과, 기존에 rf
와 xf
를 혼동해 표기한 것은 엄밀한 관점에서 잘못입니다.
rf
로 표기하는 것이 보다 정확한 것으로 판단됩니다.