在堆栈使用效率和时间方面哪个函数最好

unj*_*nj2 6 lisp functional-programming clojure

我写了3个函数来计算a-element出现在a-list中的次数.我尝试了各种输入并对其进行了分析,但我仍然不知道哪种功能在堆栈使用效率和时间效率方面最佳.请帮帮我.

;; Using an accumulator
    (defn count-instances1 [a-list an-element]
      (letfn [(count-aux [list-aux acc]
                         (cond
                           (empty? list-aux) acc
                           :else (if (= (first list-aux) an-element)  
                                   (count-aux (rest list-aux) (inc acc))
                                   (count-aux (rest list-aux) acc))))]
        (count-aux a-list 0)))

;; Normal counting 
    (defn count-instances2 [a-list an-element]
     (cond
       (empty? a-list) 0
       :else
          (if (= (first a-list) an-element )
              (+ 1 (count-instances2 (rest a-list) an-element))
              (count-instances2 (rest a-list) an-element))))

;; using loop. does this help at all?
   (defn count-instances3 [a-list an-element]
        (loop [mylist a-list acount 0]
            (if (empty? mylist)
                acount
                (if (= (first mylist) an-element)
                (recur (rest mylist)(inc acount))
                (recur (rest mylist) acount)))))
Run Code Online (Sandbox Code Playgroud)

Sva*_*nte 2

循环/递归版本是正确的方法。由于 JVM 的限制,Clojure 无法优化尾部调用。

  • 这不准确。您应该说 Clojure *选择*不优化尾部调用,因为在尚不具备尾部调用的语言(Java)中很难实现这一点。事实上,在 JVM 之上有一些语言的实现(例如,SISC - http://sisc-scheme.org/)可以优化尾部调用。 (3认同)
  • `recur` 很好,因为它是显式的,并且您只能从尾部位置使用它(编译器否则会抱怨),它会捕获您认为是尾部递归但实际上不是的实例。它都可以自动宏化,但是用符号“recur”替换尾部调用中的函数名称并不是那么麻烦。 (3认同)