클로저에서 꼬리재귀 버전인데, 클로져는 상호참조 경우 trampoine을 써서 스택을 사용하지 않게 해야 됨.
그래서 코드를 보면 #(...), (fn []...) 이런거로 해줘야 되고, 스택 버전은 그걸 함수가 아닌 그냥 코드로 만들어주면 됨.
야 근데, 산수만으로 하는건 내 머리가 안돌아가서 조합을 수집해서 처리하는 거로 만듬 (아마 이게 ㅊㄹ가 말한 배열을 쓰면 안된다는 의미인듯)
어떠냐? 정확히 내 머리가 생각한 그대로 구현하고, 꼬리재귀로 바꾸기 위해 약간 손댄건데
;;
;; ㅊㄹ의 주사위
;;
;; n개의 주사위를 던질 때 합 total 인 경우의 조합을 구하라
;;
;; (dice-combination ^Integer n ^Integer total) => ListOfDiceNumbers
;;
(ns user)
;; 상수
(defonce dice-numbers [1 2 3 4 5 6])
;; 경우의 수 만들어 주는 도우미 함수
(defn- make-combination-set [acc ns]
(if (empty? acc)
(map vector ns)
(for [a acc
n ns]
(conj a n))))
(defn dice-combination
[^Integer n0 ^Integer total]
(letfn [(find-combinations [n result] ;; 마지막인지 계속할건지 결정해주는 로컬 함수. 마지막이 아니면 상호참조 로컬함수 콜
(if (zero? n)
(filter #(= (reduce + %) total) result) ;; 마지막이면 total에 맞아떨어지는 것만 추려서 답을 리턴
#(find-combination n result)))
(find-combination [n result] ;; total을 넘지않는 후보군을 만들어주는 함수. 후보군을 만들었으면 다시 반대쪽 상호참조 로컬함수 콜
(fn []
(->> (make-combination-set result dice-numbers)
(filter #(<= (reduce + %) total))
(find-combinations (dec n)))))]
(trampoline find-combinations n0 [])))
세상에 프갤 재귀 대마왕까지 내 문제를 건드리넹 ㄷ