ClojureDocs

导航

命名空间

蹦床

clojure.core

1.0 开始可用 (来源)
  • (蹦床 f)
  • (蹦床 f & args)
trampoline can be used to convert algorithms requiring mutual
recursion without stack consumption. Calls f with supplied args, if
any. If f returns a fn, calls that fn with no arguments, and
continues to repeat, until the return value is not a fn, then
returns that non-fn value. Note that if you want to return a fn as a
final value, you must wrap it in some data structure and unpack it
after trampoline returns.
5 Examples
(defn foo [x]
   (if (< x 0)
     (println "done")
     #(foo (do (println :x x) (dec x)))))
;; #'user/foo

;; `trampoline` will keep calling the function 
;; for as long as "foo" returns a function.

(trampoline foo 10)
;; :x 10
;; :x 9
;; :x 8
;; :x 7
;; :x 6
;; :x 5
;; :x 4
;; :x 3
;; :x 2
;; :x 1
;; :x 0
;; done
;;=> nil
;; Short tutorial-style article with example of using trampoline at this link:
;; http://jakemccrary.com/blog/2010/12/06/trampolining-through-mutual-recursion/
;; Using mutually recursive functions to implement a finite state machine (FSM)
;; This machine has three states {a b c} and 
;; seven transitions {:a-b :a-c :b-a :b-c :c-a :c-b :final}.

(defn foo [cmds]
(letfn
   [(a-> [[_ & rs]]
      #(case _ 
         :a-b (b-> rs)
         :a-c (c-> rs)
         false))
    (b-> [[_ & rs]]
      #(case _ 
         :b-a (a-> rs)
         :b-c (c-> rs)
         false))
    (c-> [[_ & rs]]
      #(case _ 
         :c-a (a-> rs)
         :c-b (c-> rs)
         :final true
         false))]
  (trampoline a-> cmds)))
    
(foo [:a-b :b-c :c-a :a-c :final])
;;=> true
;; from <the joy of clojure 2nd>
;; Mutually recursive functions are nice for implementing 
;; finite state machines (FSAs)

(defn elevator [commands]
  (letfn
      [(ff-open [[_ & r]]
         "When the elevator is open on the 1st floor
          it can either close or be done."
         #(case _
            :close (ff-closed r)
            :done true
            false))
       (ff-closed [[_ & r]]
                   "When the elevator is closed on the 1st floor
                    it can either open or go up."
                   #(case _
                      :open (ff-open r)
                      :up (sf-closed r)
                      false))
       (sf-closed [[_ & r]]
                    "When the elevator is closed on the 2nd floor
                     it can either go down or open."
                    #(case _
                       :down (ff-closed r)
                       :open (sf-open r)
                       false))
       (sf-open [[_ & r]]
                  "When the elevator is open on the 2nd floor
                   it can either close or be done"
                  #(case _
                     :close (sf-closed r)
                     :done true
                     false))]

    (trampoline ff-open commands)))

(elevator [:close :open :close :up :open :open :done])
                                        ;=> false
(elevator [:close :up :open :close :down :open :done])
                                        ;=> true
;; run at your own risk!
(elevator (cycle [:close :open]))                                       
 ; ... runs forever
;; without trampoline
(declare hatt)
(defn catt [n]
  (when-not (zero? n)
    (when (zero? (rem n 100))
      (println "catt:" n))
    (hatt (dec n))))
(defn hatt [n]
  (when-not (zero? n)
    (if (zero? (rem n 100))
      (println "hatt:" n))
    (catt (dec n))))

;; std=> catt: 100000000
;; std=> catt: 99999900
;; std=> catt: 99999800
;; std=> catt: 99999700
;; std=> catt: 99999600
;; std=> catt: 99999500
;; std=> catt: 99999400
;; .
;; .
;; .
;; std => CompilerException java.lang.StackOverflowError

;; with trampoline

(declare hattr)

(defn cattr [n]
  (when-not (zero? n)
    (when (zero? (rem n 100))
      (println "cattr:" n))
    (fn [] (hattr (dec n)))))
(defn hattr [n]
  (when-not (zero? n)
    (if (zero? (rem n 100))
      (println "hatttr:" n))
    (fn [] (cattr (dec n)))))

(trampoline cattr 10000000)

;; std=> catt: 100000000
;; std=> catt: 99999900
;; std=> catt: 99999800
;; std=> catt: 99999700
;; std=> catt: 99999600
;; std=> catt: 99999500
;; std=> catt: 99999400
;; .
;; .
;; .
;; std=> catt: 100
See Also

Evaluates the exprs in a lexical context in which the symbols in the binding-forms are bound to th...

Added by kumarshantanu

Evaluates the exprs in order, then, in parallel, rebinds the bindings of the recursion point to the ...

Added by kumarshantanu

fnspec ==> (fname [params*] exprs) or (fname ([params*] exprs)+) Takes a vector of function specs...

Added by phreed