2010-07-08 14 views

Respuesta

144

Una función puede tener múltiples firmas si las firmas son diferentes en arity. Puede usar eso para proporcionar valores predeterminados.

(defn string->integer 
    ([s] (string->integer s 10)) 
    ([s base] (Integer/parseInt s base))) 

Nota que suponiendo false y nil son ambos no-valores considerados, (if (nil? base) 10 base) podría acortarse a (if base base 10) o continuación de (or base 10).

+3

Creo que sería mejor para la segunda línea de decir '(s repiten 10)', usando [ 'recur'] (http: //grimoire.arrdem .com/1.6.0/clojure.core/recur /) en lugar de repetir el nombre de la función 'string-> integer'. Eso haría que sea más fácil cambiar el nombre de la función en el futuro. ¿Alguien sabe alguna razón para no usar 'recur' en estas situaciones? –

+9

Parece que 'recurrir' solo funciona en el mismo arity. si intentó recurrir arriba, por ejemplo: 'java.lang.IllegalArgumentException: recuento de argumentos no coincidentes para recurrir, esperado: 1 args, got: 2, compilación:' – djhaskin987

+0

Se encontró con este mismo problema. ¿Tendría sentido tener la llamada a la función en sí misma (es decir '(string-> integer s 10)')? –

140

También puede desestructurar rest argumentos como un mapa desde Clojure 1.2 [ref]. Esto le permite nombrar y proporcionan valores por defecto para los argumentos de la función:

(defn string->integer [s & {:keys [base] :or {base 10}}] 
    (Integer/parseInt s base)) 

Ahora se puede llamar

(string->integer "11") 
=> 11 

o

(string->integer "11" :base 8) 
=> 9 

Esto se puede ver en acción aquí: https://github.com/Raynes/clavatar/blob/master/src/clavatar/core.clj (por ejemplo)

+1

Tan fácil de entender si proviene de un fondo de Python :) – Dan

+1

Esto es mucho más fácil de entender que la respuesta aceptada ... ¿es esto aceptado ["Clojurian"] (https://github.com/bbatsov/clojure-style -guide) manera? Por favor considere agregar a este documento. – Droogans

+1

He [agregado un problema] (https://github.com/bbatsov/clojure-style-guide/issues/37) a la guía de estilo no oficial para ayudar a solucionar esto. – Droogans

31

Esta solución es la más cercana al espíritu de la solución original, pero marginalmente limpiador

(defn string->integer [str & [base]] 
    (Integer/parseInt str (or base 10))) 

A patrón similar que puede ser usos prácticos or combinados con let

(defn string->integer [str & [base]] 
    (let [base (or base 10)] 
    (Integer/parseInt str base))) 

mientras que en este caso más detallado, que puede ser útil si desea tener valores predeterminados que dependen de otros valores de entrada. Por ejemplo, considere la siguiente función:

(defn exemplar [a & [b c]] 
    (let [b (or b 5) 
     c (or c (* 7 b))] 
    ;; or whatever yer actual code might be... 
    (println a b c))) 

(exemplar 3) => 3 5 35 

Este enfoque puede ampliarse fácilmente para trabajar con argumentos con nombre (como en la solución de M. Gilliar), así:

(defn exemplar [a & {:keys [b c]}] 
    (let [b (or b 5) 
     c (or c (* 7 b))] 
    (println a b c))) 

O utilizando aún más de una fusión:

(defn exemplar [a & {:keys [b c] :or {b 5}}] 
    (let [c (or c (* 7 b))] 
    (println a b c))) 
+0

Si no necesita que sus valores predeterminados dependan de otros valores predeterminados (o tal vez incluso si lo hace), la solución de Matthew anterior también permite múltiples valores predeterminados para diferentes variables. Es mucho más limpio que usar un 'o' normal – johnbakers

+0

de acuerdo; Voy a actualizar para reflejar esto. Gracias. – metasoarous

+0

Soy un novato de Clojure, así que tal vez OpenLearner tenga razón, pero esta es una alternativa interesante a la solución de Matthew anterior. Me alegra saber esto si finalmente decido usarlo o no. – GlenPeterson

8

Hay otro enfoque es posible que desee considerar: partial funciones. Se podría decir que son una forma más "funcional" y más flexible de especificar valores predeterminados para las funciones.

de inicio mediante la creación (si es necesario) una función que tiene el parámetro (s) que desea ofrecer por defecto (s) como el parámetro (s) que conduce:

(defn string->integer [base str] 
    (Integer/parseInt str base)) 

Esto se hace porque Clojure de La versión de partial le permite proporcionar los valores "predeterminados" solo en el orden en que aparecen en la definición de la función.Una vez que los parámetros han sido ordenados como se desee, a continuación, puede crear una versión "default" de la función utilizando la función partial:

(partial string->integer 10) 

Con el fin de realizar esta función se puede llamar varias veces se puede poner en un var usando def:

(def decimal (partial string->integer 10)) 
(decimal "10") 
;10 

también podría crear un "predeterminada local" usando let:

(let [hex (partial string->integer 16)] 
    (* (hex "FF") (hex "AA"))) 
;43350 

El enfoque de función parcial tiene una ventaja clave sobre los demás: el consumidor de la función aún puede decidir cuál será el valor predeterminado en lugar del productor de la función sin necesidad de modificar la definición de función. Esto se ilustra en el ejemplo con hex donde he decidido que la función predeterminada decimal no es lo que quiero.

Otra ventaja de este enfoque es que puede asignar a la función predeterminada un nombre diferente (decimal, hexadecimal, etc.) que puede ser más descriptivo y/o diferente (var, local). La función parcial también puede ser mezclado con algunos de los enfoques más arriba si lo desea:

(defn string->integer 
    ([s] (string->integer s 10)) 
    ([base s] (Integer/parseInt s base))) 

(def hex (partial string->integer 16)) 

(Nota esto es ligeramente diferente de la respuesta de Brian como el orden de los parámetros se ha invertido por las razones expuestas en la parte superior de esta respuesta)

+1

Esto no es lo que la pregunta está pidiendo; es interesante sin embargo. – Zaz

Cuestiones relacionadas