I am working with SICP and exercise 2.29-b gave me the opportunity to have fun with the Continuation Passing Style while traversing mobiles and branches.
To make the story short, each mobile has left and right branch, which are composed by a length and either a numeric weight or another mobile. The question asks to find the total weight given a mobile.
After the first mutually recursive solution, quite simple, I tried and successfully implemented a cps' one:
(defn total-weight-cps [mobile]
(letfn
[(branch-weight-cps
[branch kont]
(let [structure (branch-structure branch)]
(if (mobile? (branch-structure branch))
(do (println "then " structure) (kont (traverse-mobile-cps structure identity)))
(do (println "else " structure) (kont structure)))))
(traverse-mobile-cps
[mobile kont]
(branch-weight-cps (left-branch mobile)
(fn [left-weight]
(branch-weight-cps (right-branch mobile)
(fn [right-weight] (kont (+ left-weight right-weight)))))))]
(traverse-mobile-cps mobile identity)))
At this point, I have tried to apply the trampoline in order to preserve my stack. But it blows with the following exception:
java.lang.ClassCastException: sicp_clojure.2_1_exercises_2_24_2_32$total_weight_STAR_$traverse_mobile_cps__6694$fn__6695$fn__6696$fn__6697 cannot be cast to java.lang.Number
Numbers.java:126 clojure.lang.Numbers.add
.../git/sicp-clojure/src/sicp_clojure/2_1_exercises_2_24_2_32.clj:185 sicp-clojure.2-1-exercises-2-24-2-32/total-weight*[fn]
core.clj:5801 clojure.core/trampoline
core.clj:5806 clojure.core/trampoline
RestFn.java:439 clojure.lang.RestFn.invoke
.../git/sicp-clojure/src/sicp_clojure/2_1_exercises_2_24_2_32.clj:186 sicp-clojure.2-1-exercises-2-24-2-32/total-weight*
The code using trampoline, following the excellent link, is:
(defn total-weight* [mobile]
(letfn
[(branch-weight-cps
[branch kont]
(let [structure (branch-structure branch)]
(if (mobile? (branch-structure branch))
(do (println "then " structure) (kont (traverse-mobile-cps structure identity)))
(do (println "else " structure) (kont structure)))))
(traverse-mobile-cps
[mobile kont]
(branch-weight-cps (left-branch mobile)
(fn [left-weight]
(branch-weight-cps (right-branch mobile)
(fn [right-weight] #(kont (+ left-weight right-weight)))))))]
(trampoline traverse-mobile-cps mobile identity)))
And finally some sample data:
(def branch11 (make-branch 1 1))
(def branch22 (make-branch 2 2))
(def branch36 (make-branch 3 6))
(def branch43 (make-branch 4 3))
(def mobile11-43 (make-mobile branch11 branch43))
(def mobile36-22 (make-mobile branch36 branch22))
(def branch5m1143 (make-branch 5 mobile11-43))
(def branch7m3622 (make-branch 7 mobile36-22))
(def mobile5m1143-7m3622 (make-mobile branch5m1143 branch7m3622))
(total-weight* mobile5m1143-7m3622)
Why does it blow up?
Following the same link in my post, I have solved turning my implementation in:
(defn total-weight* [mobile]
(letfn
[(branch-weight-cps
[branch kont]
(let [structure (branch-structure branch)]
(if (mobile? (branch-structure branch))
(fn [] (traverse-mobile-cps structure kont))
(fn [] (kont structure)))))
(traverse-mobile-cps
[mobile kont]
(branch-weight-cps (left-branch mobile)
(fn [left-weight]
(branch-weight-cps (right-branch mobile)
(fn [right-weight] #(kont (+ left-weight right-weight)))))))]
(trampoline traverse-mobile-cps mobile identity)))
来源:https://stackoverflow.com/questions/26450982/sicp-continuation-passing-style-and-clojures-trampoline