【问题标题】:Comparing dates in list in Haskell比较 Haskell 列表中的日期
【发布时间】:2020-09-20 00:50:00
【问题描述】:

所以我创建了这个函数,它接受 2 个项目并比较它们并返回最大的日期。

biggerDate :: (Ord a1,Ord a2,Ord a3) => (a3,a2,a1)->(a3,a2,a1)->(a3,a2,a1)
biggerDate (x, x1, x2) (y, y1, y2) = 
  if x2 > y2
  then (x, x1, x2) 
  else if x1 > y1 then (x, x1, x2) else (y, y1, y2)

现在我正在尝试列出日期并比较所有日期以找到最大的日期。

我只有两个远了

maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)

我一直在尝试从列表中取出 2 个元素并进行比较。

【问题讨论】:

    标签: list date haskell compare


    【解决方案1】:

    让我们从功能上思考,让类型来指导我们。

    maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)
    maxDate xs = ???
    

    我们列出一份清单,然后……做点什么。那么,第一个问题是:如果列表为空,我们希望发生什么?有几种方法可以处理这个问题,但为了我们的目的,我们只会说这是一个错误并发出错误信号

    maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)
    maxDate [] = error "Empty list of dates!"
    maxDate (x : xs) = ???
    

    好的,下一个最简单的情况是单元素列表。在那种情况下,最大的日期显然是唯一的。毕竟,我们只有一个选择。

    maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)
    maxDate [] = error "Empty list of dates!"
    maxDate [x] = x
    maxDate (x : xs) = ???
    

    现在我们只剩下一般情况了:我们的列表中有几个元素。我使用了模式匹配x : xs 来提取第一个。我们现在要做的是找出哪个更大:第一个元素或列表的其余部分。我们可以通过递归找出列表其余部分中最大的元素是什么:maxDate xs。然后我们需要弄清楚那个东西是大于还是小于第一个元素x。您已经为此编写了一个函数,所以让我们使用它。

    maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)
    maxDate [] = error "Empty list of dates!"
    maxDate [x] = x
    maxDate (x : xs) = biggerDate x (maxDate xs)
    

    现在我们有了一个有效的实现。但我们不要止步于此。让我们变得更好。

    首先,请注意我们可以对(a3, a2, a1) 上的任何二进制函数执行此操作,而不仅仅是biggerDate。让我们稍微抽象一下我们的函数并接受一个额外的参数

    foldDate :: (Ord a1, Ord a2, Ord a3) => ((a3, a2, a1) -> (a3, a2, a1) -> (a3, a2, a1)) -> [(a3, a2, a1)] -> (a3, a2, a1)
    foldDate _ [] = error "Empty list!"
    foldDate _ [x] = x
    foldDate f (x : xs) = f x (foldDate f xs)
    

    我添加了一个额外的参数,以使我们的函数在一般情况下更有用。现在,maxDate 只是foldDate 的一个特例。

    maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)
    maxDate = foldDate biggerDate
    

    但我们可以更进一步,事实证明,foldDate 根本不需要它的参数是日期。它适用于任何类型。

    fold :: (a -> a -> a) -> [a] -> a
    fold _ [] = error "Empty list!"
    fold _ [x] = x
    fold f (x : xs) = f x (fold f xs)
    
    maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)
    maxDate = fold biggerDate
    

    现在,Haskell 中有一个非常有用的工具,叫做Hoogle。您可以将类型签名插入其中,它会告诉您与该签名匹配的内容是内置的还是在知名的 Haskell 库中。让我们插入抽象的fold 函数类型:(a -> a -> a) -> [a] -> a。第一个结果是一个名为foldr1 的函数。虽然源代码比我们在这里写的要复杂一些,但事实证明foldr1 确实是我们想要的函数。我们甚至不需要自己编写fold 函数;它已经内置了。

    maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)
    maxDate = foldr1 biggerDate
    

    太棒了! maxDate 现在只有两个字。这很时髦。但我们也可以在biggerDate 上工作。看,事实证明,Ord 有一个三元组的实例,它自动从第一个元素开始排序,然后是第二个,然后是第三个。因此,如果您愿意按 YMD 顺序(而不是像现在这样的 DMY)来定位您的日期,您的 biggerDate 也可以变得更简单。

    biggerDate :: (Ord a1,Ord a2,Ord a3) => (a3, a2, a1) -> (a3, a2, a1) -> (a3, a2, a1)
    biggerDate x y = if x > y then x else y
    

    如果您仍然希望以 DMY 格式打印日期,您始终可以使用自己的 Show 实例定义自定义数据类型 (data Date = Date Int Int Int),但现在我们将只使用 YMD,因为它使一些事情变得更简单.

    好的,但再次,让我们抽象一下。该函数对元组没有任何作用,所以它应该适用于任何Ord 的东西,对吧?

    bigger :: Ord a => a -> a -> a
    bigger x y = if x > y then x else y
    
    maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)
    maxDate = foldr1 bigger
    

    好的,这个类型签名现在看起来很抽象。让我们再次跳到Hoogle。瞧,bigger 函数也是内置的:它被称为max。所以我们甚至不需要bigger

    maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)
    maxDate = foldr1 max
    

    让我们再次抽象一下。这个版本的maxDate 也没有真正使用元组,所以它的类型签名可能和Ord a => [a] -> a 一样通用。还有一个trip through Hoogle 告诉我们,即使那个函数也是available in Haskell。所以我们最终的定义是

    maxDate :: (Ord a1, Ord a2, Ord a3) => [(a3, a2, a1)] -> (a3, a2, a1)
    maxDate = maximum
    

    maxDate 函数字面意思是 Haskell 内置的,如果您知道如何使用可用的抽象技术。

    我如此详细地介绍所有这些的原因正是如此。准备好自己编写这些递归定义是很有价值的,尤其是在开始时,我建议你这样做。事实上,仅仅通过标准库(尤其是Data.List 是一个金矿)并手动实现你在那里看到的功能是学习 Haskell 的一个很好的练习。

    但同样重要的是要注意,标准库很好地捕获了许多常见的设计模式。你的“我有一个二进制函数,我想折叠一个列表”的模式被foldr1(及其折叠函数系列)捕获,更一般地说,你的模式“我想要这个列表中最大的东西, maximum 函数捕获了一些“最大”的定义。这就是人们使用 Haskell 的原因。它的多态能力做了很多工作,并提供了一个相对较小的标准库(与 Java 相比),基于它的大小,它比乍一看更有用,并且捕获了很多更高的- 非常优雅地排序编程模式。

    【讨论】:

      猜你喜欢
      • 2011-11-28
      • 1970-01-01
      • 1970-01-01
      • 2016-04-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-14
      • 2020-03-05
      相关资源
      最近更新 更多