2009-07-22 21 views
6

Escribí 3 funciones que cuentan la cantidad de veces que aparece un elemento en una lista. Intenté varias entradas y lo perfilé, pero todavía no sé qué función es la mejor en términos de eficiencia de uso de la pila y eficiencia del tiempo. Por favor, ayúdame.Qué función es la mejor en términos de eficiencia de uso de pila y tiempo

;; 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))))) 
+3

¿Cuáles fueron los resultados de sus esfuerzos de creación de perfiles? –

+3

Anidado 'defn' probablemente no haga lo que piensas. 'defn' siempre define una función toplevel. Puede usar 'letfn' (o incluso' (let [f (fn ...)]) 'si quiere definir una función interna. –

+0

Gracias Brian. Pero no puedo hacer que la letfn funcione. ¿Podrías editar mi pregunta con letfn? Muchas gracias. – unj2

Respuesta

2

La versión de bucle/repiten es la manera correcta. Clojure no puede optimizar las llamadas finales debido a las limitaciones de la JVM.

+3

Eso no es exacto. Debería decir que Clojure * elige * no optimizar las llamadas finales, porque eso es bastante difícil de lograr en un idioma (Java) que aún no las tiene. De hecho, hay algunas implementaciones de idiomas (por ejemplo, SISC - http://sisc-scheme.org/) en la parte superior de la JVM que optimizan las llamadas finales. –

+0

Eso es realmente extraño. ¿Por qué no quiere optimizar las llamadas de cola? Nos habría ahorrado muchas molestias? ¿Hay intercambios? – unj2

+3

'recur' es bueno porque es explícito y solo se puede usar desde las posiciones finales (el compilador se queja de lo contrario) que capta las instancias en las que cree que está repitiendo la cola, pero no es así. Todo se puede eliminar macro de forma automática, pero no es tan complicado reemplazar el nombre de la función en la llamada final con el símbolo 'recurrencia'. –

0

escribir el código para que el compilador/interperter puede Optmize por recursión de cola, debe dar lugar a un cierto aumento de rendimiento y la pila de reducción de uso. Creo que su función de conteo normal podría calificar para la recursión de cola para que uno sea bastante rápido. No estoy seguro ya que solo me dedico a Lisp como hobby.

http://en.wikipedia.org/wiki/Tail_recursion

+0

Clojure no puede optimizar las llamadas finales debido a las limitaciones de la JVM. – Svante

+1

El comentario de Rich Hickey sobre esto fue que era mejor tener una abstación explícita que funcionara todo el tiempo (recurrente) que una implícita que funcionara casi todo el tiempo (debido a las complejidades de hacer esto en la JVM) –

+0

@Svante - Clojure puede optimizar y optimiza las llamadas finales. Solo tiene que indicar explícitamente que lo quiere utilizando 'recur' (que fue una elección de diseño de idioma de Rich Hickey). Es perfectamente posible hacer TCO en la JVM la mayor parte del tiempo. Las limitaciones de JVM solo afectan la co-recursión mutua (que en realidad es un caso bastante raro). – mikera

Cuestiones relacionadas