2012-04-17 24 views
40

Estoy buscando una función que devuelva el primer elemento en una secuencia que evalúa un fn como verdadero. Por ejemplo:Devolver el primer elemento en un mapa/lista/secuencia que satisface una función que devuelve verdadero

(first-map (fn [x] (= x 1)) '(3 4 1)) 

La función falsa anterior debe devolver 1 (el último elemento en la lista). ¿Hay algo así en Clojure?

+7

por qué no simplemente '(primero (filtro # (=% 1) '(3 4 1))'? – 4e6

+0

@ 4e6 Porque eso aplicaría la función a cada elemento de la lista, lo que podría ser indeseable en una lista grande . – Matthew

+10

El mapa es flojo, así que no creo que lo sea. Sin embargo, debes probarlo. – Bill

Respuesta

50
user=> (defn find-first 
     [f coll] 
     (first (filter f coll))) 
#'user/find-first 
user=> (find-first #(= % 1) [3 4 1]) 
1 

Editar: Una concurrencia. :) No. No aplica f a toda la lista. Solo a los elementos hasta la primera coincidencia debido a la pereza de filter.

+3

Gracias @kotarak, mi preocupación es que esto procesara todos los elementos de la colección, lo cual es indeseable en una lista grande. Quizás necesite crear una función que se repita hasta que encuentre la unidad que satisfaga la condición. – Matthew

+1

Veo su edición. ¡Impresionante! Muchas gracias. – Matthew

+4

@Matthew módulo de secuencias fragmentadas. Hay 'f' se puede aplicar a más elementos dependiendo de el tamaño del fragmento – kotarak

8

creo some es la mejor herramienta para el trabajo:

(some #(if (= % 1) %) '(3 4 1)) 
+1

Esto no sufre del efecto de secuencias fragmentadas. Pero no funciona en los casos en que '1' es' nil' o 'falso'. YMMV. – kotarak

+0

Espera ... ¿cómo puede 1 igual a cero? :) de todos modos, Matthew especificó que los elementos deberían 'evaluar a verdadero', así que creo que el comportamiento es el deseado. – vemv

+3

Considera 'f' ser' # (contiene? # {0 "falso" falso}%) '. Y dijo "evalúa un fn a verdad". – kotarak

40

En su caso, el lenguaje es

(some #{1} [1 2 3 4]) 

Cómo funciona: # {1} es un conjunto literal. Un conjunto también es una función que evalúa su arg si el arg está presente en el conjunto y, en caso contrario, nulo. Cualquier elemento de conjunto es un valor de "veracidad" (bueno, a excepción de un booleano falso, pero eso es una rareza en un conjunto). some devuelve el valor de retorno del predicado evaluado frente al primer miembro de la colección cuyo resultado fue verdadero.

3

Usando drop-while en lugar de filter debe abordar "sobre-aplicación" de f para secuencias fragmentados:

(defn find-first [f coll] 
    (first (drop-while (complement f) coll))) 
;;=> #'user/find-first 

(find-first #(= % 1) [3 4 1]) 
;;=> 1 
10

I probado varios métodos mencionados en este hilo (JDK 8 y Clojure 1.7), y que algunas pruebas de referencia :

repl> (defn find-first 
     [f coll] 
     (first (filter f coll))) 
#'cenx.parker.strategies.vzw.repl/find-first 

repl> (time (find-first #(= % 50000000) (range))) 
"Elapsed time: 5799.41122 msecs" 
50000000 

repl> (time (some #{50000000} (range))) 
"Elapsed time: 4386.256124 msecs" 
50000000 

repl> (time (reduce #(when (= %2 50000000) (reduced %2)) nil (range))) 
"Elapsed time: 993.267553 msecs" 
50000000 

los resultados muestran que reduce manera puede ser la solución más eficiente que en clojure 1.7.

+1

Muy interesante. Gracias por probar esto, xando. Si sería mejor tener números de [Criterium] (https://github.com/hugoduncan/criterium), pero creo que al buscar un elemento que está tan avanzado en la secuencia, la JVM ha tenido la oportunidad de optimiza el código. – Mars

Cuestiones relacionadas