일단 들어가기 전에 용어 설명부터 해보자.
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 (다만 오류가 있는 것 같다. 더 자세한 내용은 이슈를 보자)
- 클로저 소스코드