I'm trying to solve this excercise but I can't think of a solution. I need to check if a list is palindrome, taking these considerations:
If the list is simple I just need to check if it's palindrome horizontally, but if it's a nested list I need to check both, vertical and horizontal.
I also need to keep in mind that each element inside the list has to be palindrome by itself, for example:
A = [1,2,3,3,2,1] is palindrome
for this case I just created a single function that uses reverse :
unidimensional:: (Eq a) => [a] -> Bool
unidimensional [] = error"List is empty."
unidimensional xs = xs == reverse xs
also this case, for example:
B = [[1,2,1],[1,2,1]] horizontally palindrome and to check if it's palindrome vertically I just transpose it, [[1,1],[2,2],[1,1]] and the result is, yes, it is palindrome both ways.
I solved this by using the function transpose to evaluate vertically if it is palindrome and then, using the function unidimensional that I used before I check if it's palindrome horizontally, everything fine:
--Checks if it's palindrome horizontally and vertically
bidimensional:: (Eq a) => [[a]] -> Bool
bidimensional [[]] = error"Empty List."
bidimensional (xs) = if left_right xs && up_down xs then True else False
--Checks if it's palindrome horizontally:
left_right (x:xs) = if x == reverse x then
if xs /= [] then bidimensional xs else True
else False
--Checks if it's palindrome vertically:
up_down:: (Eq a) => [[a]] -> Bool
up_down (xs) = left_right ys where ys = transpose xs
transpose:: [[a]]->[[a]]
transpose ([]:_) = []
transpose x = (map head x) : transpose (map tail x)
the problem is here:
The input my program needs to receive has to be something like this:
> palindrome [[1,2,2,1], [3,7,9,9],[3,7,9,9], [1,2,2,1]]
My problem is: my function palindrome should receive as a parameter a list [a] , but palindrome should work for nested lists, like [[[a]]] or [[a]]
palindrome is the function that takes the input.
The thing is that when I get a simple list, my head, which is x is a number, and xs which is the tail, would be the rest of the numbers, and that's ok, but when palindrome receives a nested list, for example [[[2,2],[2,2]],[[1,1],[1,1]]] the head, x is now [[2,2],[2,2]] , so I can't use my function like, bidimensional because [[2,2],[2,2]] is not a list anymore, when I try to call bidimensional with xs which is [[2,2],[2,2]] I get an error: xs is now type a and not [[a]]
My question is : How can I make my function palindrome, work with any type of list (simple and nested) taking into account the error I mentioned before. Thanks in advance!
My question is: How can I make my function palindrome, work with any type of list (simple and nested) taking into account the error I mentioned before. Thanks in advance!
Let's think about this hypothetical palindrome
function and try to figure out what type signature it would need. (Thinking about a function in terms of its type signature always helps me.)
Suppose palindrome
has a signature palindrome :: [x] -> Bool
We what the following statements to be true:
palindrome [1, 2, 1] === True
palindrome [1, 2, 2] === False
palindrome [[1, 2, 1], [3, 4, 3], [1, 2, 1]] === True
palindrome [[1, 2, 2], [3, 4, 3], [1, 2, 2]] === False
In the first two assertions, palindrome
specializes to [Integer] -> Bool
, so x
is Integer
in those cases. The only sensible implementation with x === Integer
is simply to check if the supplied list of integers is a palindrome, ie, check that the first element equals the last, pop those off and repeat (or, equivalently, check that xs
equals reverse xs
as you have). We can use this algorithm whenever x
is an instance of Eq
.
In the last two properties, palindrome
specializes to [[Integer]] -> Bool
, so x
is [Integer]
there. It seems like we should be able to detect that x
is itself a list of integers, and then know that we need to recursively apply palindrome to each inner list. However, Haskell polymorphism doesn't work that way. In order for a function to be polymorphic over a type parameter x
, we need to define a single implementation of that function that works the same way no matter what type x
happens to be .
In other words, to define a polymorphic function palindrome :: [x] -> Bool
, our implementation cannot know anything about the type parameter x
. This forces us to use the same implementation when x === [Integer]
as we used when x === Integer
, which would evaluate to True
instead of False
for the final test case.
You won't be able to make your palindrome
function work the way you want it to for nested lists in Haskell if you insist on the input being standard []
types.
One thing you might do is have your palindrome
function take an extra parameter of type Int
that tells the function how deeply to check. In that case, you'd need to know ahead of time how deeply nested your input is. Another thing you might do is write your palindrome
function to take input in some other data structure besides []
, like a Tree
, or something with arbitrary nesting. Maybe you can write your function to accept a Value
, a type that represents arbitrary JSON values from the popular library Aeson .
Note: It's probably a good idea for palindrome []
to be True
, no?
You could use a typeclass.
class Palindrome a where
palindrome :: a -> Bool
instance Palindrome Integer where
palindrome _ = True
instance (Eq a, Palindrome a) => Palindrome [a] where
palindrome xs = xs == reverse xs && all palindrome xs
For a two-dimensional list, you are checking if the list is a palindrome, "both horizontally and vertically". This can be done without the transpose
function: the list is a palindrome "vertically" if the list is a palindrome, and the list is a palindrome "horizontally" if every sublist is a palindrome. The second instance
checks both.
For a one-dimensional list, the second instance
simply checks if the list is a palindrome. In this case, the && all palindrome xs
might as well not be there: since the first instance
specifies that Integer
s are always palindromes, all palindrome xs
always evaluates to True
. This can be seen as the "base case" for this recursive algorithm.
This will work for any depth of nested lists. You can also instantiate the class for other base data types, or (I'm pretty sure) even for ALL types of class Eq
(although this leads to overlapping instances which is its own can of worms).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.