比较Haskell中列表中的日期

问题描述

因此,我创建了此函数,该函数接受2个项目并进行比较并返回最大日期。

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

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

我只有两个远的地方

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

我坚持尝试从列表中删除2个元素并进行比较。

解决方法

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

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

我们列出一个清单,然后做些事情。好吧,第一个问题是:如果列表为空,我们想发生什么?有几种方法可以解决此问题,但是出于我们的目的,我们只说那是一个错误并发出错误信号

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

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

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

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

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

现在我们有了一个可行的实现。但是,我们不要就此止步。让我们做得更好。

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

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

我添加了一个附加参数,以使我们的函数总体上更有用。现在,maxDate只是foldDate的特例。

maxDate :: (Ord a1,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,a1)
maxDate = fold biggerDate

现在,Haskell中有一个非常有用的工具,称为Hoogle。您可以在其中插入类型签名,它会告诉您是否内置了与该签名匹配的东西或在著名的Haskell库中。让我们插入抽象的fold函数的类型:(a -> a -> a) -> [a] -> a。最早的结果之一是称为foldr1的函数。尽管源代码比我们在这里写的要复杂一些,但事实证明foldr1实际上是我们想要的确切函数。我们甚至不需要自己编写fold函数;它已经内置了。

maxDate :: (Ord a1,a1)
maxDate = foldr1 biggerDate

太好了! maxDate现在只有两个字。那真是时髦。但是我们也可以从事biggerDate的工作。事实证明,Ord拥有一个三元组事物的实例,该实例自动从第一个元素开始,然后是第二个元素,然后是第三个元素进行排序。因此,如果您愿意按照YMD顺序排列日期(而不是像现在那样用DMY定位),则biggerDate也可以变得更简单。

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

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

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

bigger :: Ord a => a -> a -> a
bigger x y = if x > y then x else y

maxDate :: (Ord a1,a1)
maxDate = foldr1 bigger

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

maxDate :: (Ord a1,a1)
maxDate = foldr1 max

让我们再次抽象。此版本的maxDate也不使用元组,因此其类型签名可能与Ord a => [a] -> a一样通用。还有一个trip through Hoogle告诉我们,该功能也是available in Haskell。所以我们的最终定义是

maxDate :: (Ord a1,a1)
maxDate = maximum

如果您知道如何使用抽象技术,函数maxDate内置在Haskell中。

我之所以详细介绍所有这些内容的原因正是这样。准备自己编写这些递归定义非常有价值,尤其是在起步时,我建议您这样做。实际上,在学习Haskell时,只需遍历标准库(尤其是Data.List就是金矿)并手动实现在那里看到的功能,就是很棒的练习。

但是还要注意,标准库很好地捕获了许多常见的设计模式。 foldr1(及其折叠函数系列)捕获了您的模式“我有一个二进制函数,我想折叠一个列表”,更一般地说,您的模式“我想要这个最大的东西” maximum函数捕获了“最大”的定义。这就是为什么人们使用Haskell的原因。它具有多态性功能,并且提供了一个相对较小的标准库(与Java相比),基于它的大小,它比乍一看看上去有用得多,并且捕获了很多顺序编程模式非常优雅。