클로저에서 꼬리재귀 버전인데,  클로져는 상호참조 경우 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 [])))