Sean Roberts
Sean Roberts

Reputation: 37

How to fix palindrome function in Haskell

I'm trying to figure out why my palindrome function isn't working. Does it have to do with how I'm declaring 'w'? I'm attempting to take out all the letters and digits in a string, convert them to uppercase, and places it in a new string 'w'. I attempt to see if they're the same by checking if 'w' is the same as it is reverse

isPalindrome :: [Char] -> Bool
isPalindrome [] = True
isPalindrome [a] = True
isPalindrome (x:xs) | w == reverse w = True
                    | otherwise = False
                         where w        | isDigit x = x:(isPalindrome xs)  -- figured isDigit and isAlpha would be okay to use
                                        | isAlpha (toUpper x) = x:(isPalindrome xs)  -- put this under so digits wouldn't have to do toUpper
                                        | otherwise = isPalindrome xs

Obviously I expect the result to return true if it's a palindrome and false otherwise.

The error I'm getting currently is:

    * Couldn't match expected type `[Char]' with actual type `Bool'
    * In the expression: isPalindrome xs
      In an equation for `w':
          w | isDigit x = x : (isPalindrome xs)
            | isAlpha (toUpper x) = x : (isPalindrome xs)
            | otherwise = isPalindrome xs
      In an equation for `isPalindrome':
          isPalindrome (x : xs)
            | w == reverse w = True
            | otherwise = False
            where
                w | isDigit x = x : (isPalindrome xs)
                  | isAlpha (toUpper x) = x : (isPalindrome xs)
                  | otherwise = isPalindrome xs
   |
48 |                                         | otherwise = isPalindrome xs
   |

Upvotes: 2

Views: 441

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476750

You try to do too much in a single function. Your w is supposed to be a string. But you define w as:

where w | isDigit x = x:(isPalindrome xs)
        | isAlpha (toUpper x) = x:(isPalindrome xs)
        | otherwise = isPalindrome xs

You here thus write a condition, which is fine, but then you construct a list that starts with x (in the first two cases), and has isPalindrome xs as tail. But isPalindrome xs is not a String, it should be, given the function signature, be a Bool.

That being said, I think you make things too complicated. We can make a String where we filter out the characters that are not digits or alpha characters with:

filter (\x -> isDigit x || isAlpha x)

or as @melpomene says, we can use isAlphaNum :: Char -> Bool:

filter isAlphaNum

So we can write such function as:

import Data.Char(isAlphaNum)

isPalindrome :: String -> Bool
isPalindrome s = w == reverse w
    where w = filter isAlphaNum s

We can use applicative style here, and work with:

import Control.Applicative(ap)
import Data.Char(isAlphaNum)

isPalindrome :: String -> Bool
isPalindrome = ((==) <*> reverse) . filter isAlphaNum

We do not need to write cases for the empty list, or a singleton list, since these will be covered by the w == reverse w: the reverse of an empty list and singleton list is an empty and singleton list respectively.

You can implement filtering yourself, but I suggest you do that in a separate function, not trying to do this in the same function. In fact it might make sense to separate the preprocessing step (removing characters that are non-digits and non-alphas from the string) in a separate function.

Upvotes: 2

Related Questions