【问题标题】:Haskell polymorphic function that checks if a list is palindrome检查列表是否为回文的 Haskell 多态函数
【发布时间】:2018-08-26 02:43:57
【问题描述】:

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

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

对于这种情况,我刚刚创建了一个使用 reverse 的函数:

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]] 结果是,是的,它是双向回文。

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

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

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

palindrome 是接受输入的函数。

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

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

【问题讨论】:

  • 您能告诉我们您的确切问题陈述吗? “[使]我的函数回文适用于任何类型的列表(简单和嵌套)”听起来像是一个相当奇怪的要求。有一些方法可以实现这样的目标,但它们不会像你想要的那样漂亮或方便。
  • 声明说,给定输入:>回文 [[1,2,2,1], [3,7,9,9],[3,7,9,9], [ 1,2,2,1]],输出应该是这样的: > 列表是水平和垂直回文。我必须对函数 palindome 进行编码,即:palindrome :: [a] ->(这里是一个 Bool 字符串,表示该列表是回文)考虑到该函数应该适用于单个列表和嵌套列表,
  • if left_right xs && up_down xs then True else False 是写left_right xs && up_down xs 的更复杂的方式。另外,写if x then y else False 是写x && y 的复杂方式。

标签: haskell functional-programming palindrome


【解决方案1】:

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

让我们考虑一下这个假设的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,因此在这些情况下xIntegerx === Integer 唯一合理的实现是简单地检查提供的整数列表是否是回文,即检查第一个元素是否等于最后一个元素,弹出并重复(或者,等效地,检查 xs 是否等于 @ 987654332@ 你有)。只要xEq 的实例,我们就可以使用这个算法。

在最后两个属性中,palindrome 专门用于[[Integer]] -> Bool,所以x[Integer]。似乎我们应该能够检测到x 本身就是一个整数列表,然后知道我们需要递归地将回文应用于每个内部列表。然而,Haskell 多态性不是这样工作的。为了使函数在类型参数x 上具有多态性,我们需要定义该函数的单个实现,该实现以相同的方式工作无论x 碰巧是什么类型

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

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

您可能会做的一件事是让您的palindrome 函数采用Int 类型的额外参数,告诉该函数检查的深度。在这种情况下,您需要提前知道您的输入嵌套的深度。您可能会做的另一件事是编写您的palindrome 函数以在[] 之外的其他数据结构中获取输入,例如Tree,或具有任意嵌套的东西。也许你可以编写你的函数来接受Value,这是一种代表流行库Aeson中任意JSON值的类型。

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

【讨论】:

    【解决方案2】:

    您可以使用类型类。

    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 指定 Integers 总是回文,all palindrome xs 总是计算为 True。这可以看作是这种递归算法的“基本情况”。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-01
      • 2017-12-11
      • 1970-01-01
      • 2013-04-09
      • 2012-01-10
      相关资源
      最近更新 更多