es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Expresar un patrón “caso … de” de forma más elegante en Haskell.

Me encontré con un patrón que creo que puede ser expresado de manera más elegante:

Tengo dos funciones f1, f2:: Int -> Int (su implementación no es relevante), y un process:: Int -> Int que hace lo siguiente:

  • Si f1 x produce un x1 diferente de x, entonces repetir el proceso con x1.
  • De lo contrario, si f2 x produce un x2 diferente de x, entonces repetir el proceso con x2.
  • Finalmente, detener el proceso y devolver x.

Mi implementación de case ... of es la siguiente:

f1:: Int -> Int
f1 = undefined

f2:: Int -> Int
f2 = undefined

process:: Int -> Int
process x =
    case f1 x of
        x ->
            case f2 x of
                x -> x
                x' -> process x'
        x' -> process x'

que produce las siguientes advertencias:

so.hs:13:17: warning: [-Woverlapping-patterns]
    Pattern match is redundant
    In a case alternative: x' -> ...
   |
13 |                 x' -> process x'
   |                 ^^^^^^^^^^^^^^^^

so.hs:14:9: warning: [-Woverlapping-patterns]
    Pattern match is redundant
    In a case alternative: x' -> ...
   |
14 |         x' -> process x'
   |         ^^^^^^^^^^^^^^^^

¿Alguien puede arrojar algo de luz sobre qué patrones están superpuestos y cómo implementar process de manera más elegante?

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Los patrones que se superponen son x' -> procesar x' en ambos casos. Esto se debe a que si f1 x produce x' diferente de x, entonces se ejecutará el primer caso y se verificará el segundo caso, pero si f2 x también produce x' diferente de x, entonces el segundo caso se ejecutará nuevamente y se hará la misma llamada recursiva procesar x'. Esto se puede solucionar fusionando los dos casos anidados en uno solo utilizando la función orElse del módulo Control.Monad, que prueba el segundo argumento si el primero devuelve Nothing:

    import Control.Monad (orElse)
    
    procesar :: Int -> Int
    procesar x =
        let x1 = f1 x
            x2 = f2 x
        in case () of
            _ | x1 /= x -> procesar x1
              | x2 /= x -> procesar x2
              | otherwise -> x
    

    Esta implementación primero calcula x1 y x2 y luego utiliza una única expresión case con tres guardias para verificar si alguno de ellos es diferente de x. Si es así, se llama a procesar recursivamente con el nuevo valor, de lo contrario se devuelve x. Esto evita el mensaje de advertencia de patrones superpuestos y hace que el código sea ligeramente más conciso y legible.

Comments are closed.