일단 들어가기 전에 용어 설명부터 해보자.
reducing function
: reduce를 할 때 인자로 넘기는 함수, 인자 두개(누적된 결과값과 새로운 값)를 받아 새로운 결과값을 반환한다.transducer
:reducing function
을 받아서 새로운reducing function
을 만드는 함수.xform
이나xf
로 불리기도 한다.
transducer
는 다음과 형태를 가진다.
(fn [rf]
(fn
([] ...)
([result] ...)
([result input] ...)))
실제 transducer
를 만드는 함수를 보자.
(defn filter
...
([pred]
(fn [rf]
(fn
([] (rf))
([result] (rf result))
([result input]
(if (pred input)
(rf result input)
result)))))
...)
위 코드를 보면 filter
가 pred
하나만 인자를 받을 때 transducer
를 반환한다는 것을 알 수 있다.
반환하는 transducer
를 보면 rf
를 인자로 받고 또다른 함수를 반환하는 것을 볼 수 있다.
즉, rf
는 reducing function
를 지칭하고 반환하는 또다른 함수는 또다른 reducing function
인다.
다만 반환되는 함수는 그냥 reducing function
은 아니다. 이 함수는 세개의 arity(위에서 []
, [result]
, [result input]
)를 가지고 이들은 각각 목적을 지닌다.
- 초기화(init
[]
):rf
의 초기화 arity를 호출한다. - 단계(step,
[result input]
):reducing function
으로rf
의 단계 arity를transducer
에 알맞게 호출할거로 예상된다. 위 같은 경우(pred input)
가 참일 경우에는 단계 arity를 호출하고 아니라면 호출하지 않는다. - 완료(completion,
[result]
): 끝날때에 호출된다.rf
의 완료 arity를 딱 한번 호출해야한다.
transduce
함수를 보면 이 과정이 어떻게 진행되는지 볼 수 있다.
(defn transduce
...
([xform f coll] (transduce xform f (f) coll)) ;(1)
([xform f init coll]
(let [f (xform f) ;(2)
ret (if (instance? clojure.lang.IReduceInit coll)
(.reduce ^clojure.lang.IReduceInit coll f init) ;(3)
(clojure.core.protocols/coll-reduce coll f init))] ;(3)
(f ret)))) ;(4)
init
인자가 없으면f
의 초기화 arity를 호출함을 알 수 있다.- 또한
transducer
인xform
에reducing function
인f
를 인자로 넘겨 (xform f)
로 reduce를 진행한다.- 마지막으로 완료 arity를 호출하는 것도 볼 수 있다.
- 클로저 레퍼런스: https://clojure.org/reference/transducers (다만 오류가 있는 것 같다. 더 자세한 내용은 이슈를 보자)
- 클로저 소스코드