2011-12-10 12 views
13

He creado una función en la que puedo usar (hasta donde yo sé) expresiones de casos o guardias.¿Cuál es el método preferido para escribir "guardias"?

foo a b c = case a of 1 -> [...] 
         2 -> [...] 
         3 -> [...] 
         [...] 
         otherwise -> error "..." 

o

foo a b c | a == 1 = [...] 
      | a == 2 = [...] 
      | a == 3 = [...] 
      | [...] 
      | otherwise = error "..." 

Entonces, la pregunta es: ¿cuál de los 2 (caja o guardias) son “mejor” la codificación? ¿Ambos son básicamente lo mismo?

+10

En su primer ejemplo, no debería usar 'de otra manera'. Lo que hace es que introducirá una nueva variable llamada 'otherwise = a', que puede conducir a errores sutiles. Debería hacer '_ -> error" ... "' en su lugar. – dflemstr

+0

@dflemstr gracias por la información y la explicación. – Nomics

+1

Como consejo general en varios idiomas, prefiero escribir subbloques de código en la siguiente línea y siempre con el mismo nivel de sangría. Esto te protege de tener que volver a enhebrar las líneas restantes si la primera cambia de longitud (tal vez debido a que se renombra o algo así) – hugomg

Respuesta

13

El primero se considera mejor estilo, por 2 razones.

En primer lugar: Mucha gente diría que se ve mejor, ya que no tiene que escribir todo el ==. Esta es una razón muy subjetiva, por supuesto. También, que normalmente ni siquiera introducir una nueva declaración del caso, pero sólo coincidir con los argumentos de la lista argumento de la función, así:

foo 1 b c = ... -- etc 
... 
foo _ b c = ... -- for the "otherwise" part 

Esto hace que el código aún más compacto y de fácil lectura, que mucha gente le gusta.

En segundo lugar, en realidad hay una diferencia semántica. Imagine que tiene un tipo de datos de esta manera:

data Cake = Apple | Cheese | Cream 

Si utiliza el primer método, partido contra los constructores en la expresión case..of:

case a of 
    Apple -> "fruit" 
    _  -> "not fruit" 

Sin embargo, si se intenta hacer una vigilado expresión de algún tipo, como esto:

| a == Apple = "fruit" 
| otherwise = "not fruit" 

... no va a funcionar realmente, porque el tipo Cake no tiene una instancia Eq, por lo que no puede usar == para comparar dos valores. No siempre es necesario introducir una instancia Eq (con deriving (Eq) después de la definición de datos), por lo que no tener que hacerlo en este caso podría ser significativo.

+0

¿Cómo podría decir que uno es mejor que el otro si son semánticamente diferentes? –

+1

@ AdamWagner porque en muchas situaciones, tiene la opción de usar cualquiera con exactamente los mismos resultados ("comparando el código de máquina resultante"). – dflemstr

+0

¡La cuestión semántica es nueva para mí! ¡¡Bien pensado!! O_o – Nomics

9

Cuando un protector puede reescribirse como una declaración de caso (sin protección) en uno de los parámetros, no es realmente necesario. Es decir. puede simplemente escribirlo como:

foo 1 b c = [...] 
foo 2 b c = [...] 
foo 3 b c = [...] 
[...] 

Cuál es la forma preferida de escribirlo. Utilizarías guardias cuando la condición que deseas no se pueda expresar como un patrón. Y usaría un enunciado de caso cuando necesita hacer coincidir algo que no sea uno de los parámetros.

+0

En realidad, tengo algunos casos base (creo que puedo llamarlos caso base - Estoy un principiante en la codificación), como "if a == 1 && b> c then (...)". Entonces, si aplicas tu sugerencia, podría hacer que mi código sea un poco menos legible, supongo (?). – Nomics

+0

En realidad (parte 2) ... Espero que pueda enviar su sugerencia ... ¡y se ve bien! ^^ – Nomics

Cuestiones relacionadas