Trampoline 如何与 Clojure 中的多种方法一起工作

How trampoline work with multimethods in Clojure

我试图了解 trampoline 如何使用尾递归来支持相互递归。但是,当下面的两个示例都以相同的结果编译时,我迷路了。我相信每个 defmethod 都必须 return 一个函数,如下面的示例 2 所示。但这显然不是这种情况,因为示例 1 工作得很好。那么尾递归下面的两个实现是否在性能上相同或者它们之间有什么区别?

示例 1:

(defmulti jump :beff)
(defmethod jump 1 [{:keys [beff]}]                                         
      (print beff)                                                             
      (jump {:beff (inc beff)}))                                               
(defmethod jump 2 [{:keys [beff]}]                                         
      (print beff)                                                             
      (jump {:beff (inc beff)}))                                               
(defmethod jump :default [{:keys [beff]}]                                  
      (print beff))                                                            
(def v {:beff 1})     
(trampoline jump v) 

示例 2:

(defmulti jump :beff)
(defmethod jump 1 [{:keys [beff]}]                                         
      (print beff)                                                             
      #(jump {:beff (inc beff)}))                                               
(defmethod jump 2 [{:keys [beff]}]                                         
      (print beff)                                                             
      #(jump {:beff (inc beff)}))                                               
(defmethod jump :default [{:keys [beff]}]                                  
      #(print beff))                                                            
(def v {:beff 1})     
(trampoline jump v)

例1错误,例2正确。

第一个之所以有效,是因为它只重复出现两次并且不会破坏堆栈。对 trampoline 的调用什么都不做,因为该函数从来 returns 一个函数,所以 trampoline 函数只是 returns 它返回的值。

它的工作方式与此相同:

user> (trampoline #(inc 41))
42

在这种情况下,trampoline 对函数进行初始调用,然后检查返回值是否为函数。由于结果是数字 42,它只是 returns 这个。在示例 1 中,trampoline 进行了第一次调用,该调用一直在其自身中重复出现,并且 returns 不是函数的值。 Trampoline 然后 returns 这个值根本没有 "bouncing"。这是另一个以同样的方式做错的例子:

user> (trampoline (fn example
                    ([] (example 4))
                    ([x] (if (pos? x) (do (println x) (example (dec x)))))))
4
3
2
1
nil