2011-09-20 15 views
32

Common Lisp tiene return-from; ¿Hay algún tipo de return en Clojure para cuando quiere regresar temprano de una función?¿Cómo regresas de una función temprano en Clojure?

+5

código Clojure tiende a no ser estructurado como una serie de declaraciones, por lo que regresar temprano no es necesariamente tan significativa . Si desea dar un ejemplo de dónde lo desea, las personas podrían sugerir alternativas. – Chuck

Respuesta

20

No hay ninguna declaración de devolución explícita en clojure. Podrías hackear algo juntos usando una combinación catch/throw si lo deseas, pero como clojure es mucho más funcional que el ceceo común, las posibilidades de que necesites un retorno temprano justo en el medio de un bloque anidado son mucho más pequeñas que en CL. La única 'buena' razón que puedo ver para declaraciones de devolución es cuando se trata de objetos mutables de una manera que no es idiomática en clojure.

No llegaría tan lejos como para decir que nunca es útil, pero creo que en Clojure, si su algoritmo necesita una declaración de devolución, es un gran olor a código.

+3

Gracias por la sugerencia. Como un ejemplo concreto: al analizar args de línea de comando, quería verificar si cada opción era válida. Al principio, traté de verificar cada opción, si una opción no era válida, entonces quería devolver un mensaje útil al usuario antes de continuar validando el resto de las opciones. Después de realizar el retorno en medio de la función es un olor a código, escribí una función "validar" que devuelve una lista de mensajes (un mensaje por cada valor de opción no válido). Si la lista de mensajes devueltos por validate está vacía, entonces todas las opciones se validaron correctamente. ¡Mucho mejor después del refactor! – Upgradingdave

0

En resumen, no. Si esto es un verdadero problema para usted, entonces usted puede conseguir alrededor de él con "the maybe monad" mónadas tienen una alta sobrecarga intelectual por lo que para muchos casos clojurians tienden a evitar el estilo "si el retorno fallido" de la programación.

Ayuda a dividir la función en funciones más pequeñas para reducir la fricción.

13

No soy un experto en Clojure, pero parece que no tiene esas construcciones para intentar ser más funcional. Echar un vistazo a lo que Stuart Halloway dice here:

Common Lisp también es compatible con un retorno de lo macro a "retorno" de la medio de una función. Esto fomenta un estilo imperativo de la programación , que Clojure desalienta.

Sin embargo, puede resolver los mismos problemas de otra manera. Aquí es el retorno de ejemplo, reescrito en un estilo funcional para que no -de retorno es necesaria:

(defn pair-with-product-greater-than [n] 
(take 1 (for [i (range 10) j (range 10) :when (> (* i j) n)] [i j]))) 

Es decir, utilizar secuencias perezosos y los valores que vuelven sobre la base de condiciones.

17

A menos que esté escribiendo un código realmente funky, la única razón por la que querrá volver temprano es si se cumple alguna condición. Pero como las funciones siempre devuelven el resultado de la última forma evaluada, if ya es esta función; simplemente coloque el valor que desea devolver en el cuerpo del if y devolverá ese valor si se cumple la condición.

0

El if opción ya dada es probablemente la mejor opción, y la nota ya que los mapas son fáciles, usted siempre puede volver {:error "blah"} en la condición de error, y {result: x} en la condición válida.

0

No hay una declaración de devolución en Clojure. Incluso si elige no ejecutar algún código utilizando una construcción de flujo como if o when, la función siempre devolverá algo, en estos casos nil. La única salida es arrojar una excepción, pero incluso así, o bien burbujeará completamente y matará a su hilo, o se verá atrapado por una función, que devolverá un valor.

19

Cuando necesite salir temprano de un cálculo, necesita una forma de hacerlo, no un argumento de los puristas.Por lo general, lo necesita cuando está reduciendo una gran colección y un cierto valor indica que no tiene sentido seguir procesando la colección. Con ese fin, el siempre práctico Clojure proporciona la función reduced.

Un ejemplo simple para ilustrar es que al multiplicar una secuencia de números, si encuentra un cero, ya sabe que el resultado final será cero, por lo que no necesita mirar el resto de la secuencia. He aquí cómo usted código que con reduced:

(defn product [nums] 
    (reduce #(if (zero? %2) 
       (reduced 0.0) 
       (* %1 %2)) 
      1.0 
      nums)) 

reduced envuelve el valor que le dan en una estructura de datos centinela para que reduce sabe que dejar de leer desde la recogida y devuelva el valor reduced en este momento. ¡Oye, es puramente funcional, incluso!

Se puede ver lo que está pasando, si se coloca la anterior if en un do con un (println %1 %2):

user=> (product [21.0 22.0 0.0 23.0 24.0 25.0 26.0]) 
1.0 21.0 
21.0 22.0 
462.0 0.0 
0.0 

user=> (product [21.0 22.0 23.0 24.0 25.0 26.0]) 
1.0 21.0 
21.0 22.0 
462.0 23.0 
10626.0 24.0 
255024.0 25.0 
6375600.0 26.0 
1.657656E8 
+5

Esta debería ser la respuesta correcta. "Cuando necesitas salir temprano de un cálculo, necesitas una forma de hacerlo, no un argumento de los puristas". La eficiencia es siempre una necesidad. Período. Ejemplo de una función para verificar si una identificación está duplicada en un conjunto de mapas: (defn duplicado? [Id myset] (reduce (fn [av] (if (= (: id v) id) (reducido verdadero) falso)) falso myset)) – Chocksmith

Cuestiones relacionadas