Tarea: Comparar expresiones intermedias con == en Haskell

He escrito el siguiente código para probar si una lista es un palíndromo o no. Para mi sorpresa, no compila con el error "No hay instancia para (Eq a) que surja de un uso de == ....". Mi suposición es que el compilador no sabe que leftHalf y rightHalf son listas.

    isPalindrome :: [a] -> Bool
    isPalindrome [] = False
    isPalindrome xs = if (leftHalf == reverse rightHalf)
              then True
              else False
     where leftHalf = take center xs
           rightHalf = drop center xs
           center = if  even (length xs)
                       then (length xs) `div` 2
                   else ((length xs) - 1) `div` 2 

1) ¿Cómo le digo al compilador que leftHalf y rightHalf son listas?
2) ¿Cómo usaría la coincidencia de patrones u otras características del lenguaje haskell para resolver esto?

EDITAR: Gracias a todos por sus comentarios. Mención especial a Matt Fenwick por el enlace de documentación y Duri por la elegante sugerencia. Escribiré las soluciones finales a continuación por si acaso.

     isPalindrome' :: (Eq a) => [a] -> Bool
     isPalindrome' [] = False
     isPalindrome' xs = if p then True else False
                   where p = leftHalf == rightHalf
                         leftHalf = take c xs
                         rightHalf = take c (reverse xs)
                         c = div l 2
                         l = length xs

isPalindrome' se puede mejorar como señaló Demi

      isPalindrome'' :: (Eq a) => [a] -> Bool
      isPalindrome'' [] = False
      isPalindrome'' xs = if (reverse xs) == xs then True else False

preguntado el 03 de mayo de 12 a las 15:05

Solo piensa en lo que xs y reverse xs se verá como para cada palíndromo y su código será mucho más simple. -

por cierto, este código es incorrecto (después de compilarlo): map isPalindrome [[1], [1,2,1]] is [False, False]. -

La div se redondeará hacia abajo, por lo que el even la prueba es innecesaria. Simplemente divide el número impar por 2 y obtendrás la misma respuesta. Sin embargo, como señala @Matt, su algoritmo no funciona cuando la longitud es impar, ya que no desea que el elemento central esté en el lado izquierdo or la mitad derecha. El take necesita redondear hacia abajo, y el drop necesita redondear. -

Tenga en cuenta que su definición de center es el mismo que center = div (length xs) 2. -

Tenga en cuenta que su uso de take y drop puede ser reemplazado por splitAt. (Por supuesto, para que su función funcione, necesita cambiar su take y drop llamadas.) -

3 Respuestas

Para probar si dos listas son iguales, debe ser posible probar si los elementos de la lista son iguales. Por lo tanto, su lista de tipo [a] debe asegurarse de que a es una instancia de Eq.

Además, por una cuestión de estilo:

x = if c then True else False

puede ser reemplazado con

x = c

contestado el 03 de mayo de 12 a las 15:05

Primero eche un vistazo al sitio web de la página Eq clase de tipo:

ghci> :i (==)
class Eq a where
  (==) :: a -> a -> Bool
  ...
    -- Defined in GHC.Classes
infix 4 ==

Lo que necesitas es una restricción de tipo on isPalindrome.

Además, este código

if (leftHalf == reverse rightHalf)
              then True
              else False

es innecesariamente largo.

contestado el 03 de mayo de 12 a las 15:05

Gracias tu respuesta ha sido muy esclarecedora. - mahara

Debes cambiar tu tipo a:

isPalindrome :: Eq a => [a] -> Bool

Y esto es realmente extraño para encontrar el centro, basta con escribir xs == reverse xs - cuando estas computando length xs repasas toda la lista y no hay economía.

contestado el 03 de mayo de 12 a las 19:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.