Haskell多态函数,用于检查列表是否为回文

时间:2018-03-16 23:22:20

标签: haskell functional-programming palindrome

我正试图解决这个问题,但我想不出解决方案。 我需要检查列表是否是回文,考虑以下因素:
如果列表很简单,我只需要检查它是否是水平的回文,但如果它是嵌套列表,我需要同时检查垂直和水平。
我还需要记住列表中的每个元素都必须它本身就是回文,例如:

A = [1,2,3,3,2,1] 是回文

对于这种情况,我刚刚创建了一个使用反向

的函数
unidimensional:: (Eq a) => [a] -> Bool
unidimensional [] = error"List is empty."
unidimensional xs = xs == reverse xs

也是这种情况,例如:

B = [[1,2,1],[1,2,1]] 水平回文并检查它是否是垂直回文我只是转置它, [[1,1], [2,2],[1,1]] ,结果是,是的,两种方式都是回文。

我通过使用transpose函数来解决这个问题,如果是palindrome则垂直评估,然后使用我在检查它是否是水平回文之前使用的一维函数,一切都很好:

--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)



问题出在这里:

我的程序需要接收的输入必须是这样的:

> palindrome [[1,2,2,1], [3,7,9,9],[3,7,9,9], [1,2,2,1]]

我的问题是:我的函数回文应该作为参数接收列表 [a] ,但回文应该适用于嵌套列表,例如 [[[a]]] [[a]]

回文是获取输入的函数。

问题是,当我得到一个简单的列表时,我的头部是 x 是一个数字, xs 是尾部,其余的数字是,并且没关系,但是当回文收到嵌套列表时,例如 [[[2,2],[2,2]],[[1,1],[1,1]]] 头部, x 现在是 [[2,2],[2,2]] ,所以我不能像二维一样使用我的函数,因为 [ [2,2],[2,2]] 不再是一个列表,当我尝试使用 xs 调用bidimensional时, [[2,2],[ 2,2]] 我收到错误: xs 现在输入 a 而不是 [[a]]

我的问题是:我如何制作我的函数回文,使用任何类型的列表(简单和嵌套),同时考虑到我之前提到的错误。提前谢谢!

2 个答案:

答案 0 :(得分:0)

  

我的问题是:我如何制作我的函数回文,使用任何类型的列表(简单和嵌套)考虑到我之前提到的错误。提前谢谢!

让我们考虑这个假设的palindrome函数,并尝试找出它需要的类型签名。 (根据类型签名考虑功能总是对我有帮助。)

假设palindrome有签名palindrome :: [x] -> Bool

我们以下陈述是真实的:

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

在前两个断言中,palindrome专门针对[Integer] -> Bool,因此x在这些情况下为Integerx === Integer唯一明智的实现只是检查提供的整数列表是否是回文,即检查第一个元素是否等于最后一个,弹出那些并重复(或等效地检查{{1}你有平等xs)。只要reverse xsx的实例,我们就可以使用此算法。

在最后两个属性中,Eq专门针对palindrome,因此[[Integer]] -> Boolx。看起来我们应该能够检测到[Integer]本身就是一个整数列表,然后知道我们需要递归地将回文应用于每个内部列表。但是,Haskell多态性并不是那样的。为了使函数在类型参数x上具有多态性,我们需要定义该函数的单个实现,其工作方式与相同,无论x类型是什么

换句话说,要定义多态函数x,我们的实现对类型参数palindrome :: [x] -> Bool一无所知。这迫使我们在x时使用x === [Integer]时使用相同的实现,x === Integer在最终测试用例时评估为True而不是False

如果您坚持输入为标准palindrome类型,那么您将无法使您的[]函数按照您希望的方式在Haskell中嵌套列表工作。

您可能要做的一件事是让palindrome函数采用类型Int的额外参数来告诉函数检查的深度。在这种情况下,您需要提前知道输入的嵌套程度。您可能要做的另一件事是编写palindrome函数以在[]之外的其他数据结构中输入输入,例如Tree或任意嵌套的内容。也许您可以编写函数来接受Value,这是一种表示来自热门库Aeson的任意JSON值的类型。

注意: palindrome [] True可能是个好主意,不是吗?

答案 1 :(得分:0)

你可以使用类型类。

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

对于二维列表,您正在检查列表是否是回文,“水平和垂直”。这可以在没有transpose函数的情况下完成:如果列表是回文,则列表是“垂直”的回文,如果每个子列表是回文,则列表是“水平”的回文。第二个instance检查两者。

对于一维列表,第二个instance只检查列表是否是回文。在这种情况下,&& all palindrome xs也可能不存在:由于第一个instance指定Integer始终是回文,all palindrome xs始终评估为True 。这可以看作是这种递归算法的“基本情况”。

这适用于任何深度的嵌套列表。您还可以为其他基本数据类型实例化该类,或者(我很确定)即使对于所有类型的类Eq也是如此(尽管这会导致重叠的实例,这是它自己的蠕虫)。