list - 比较 Haskell 列表中的日期
问题描述
所以我创建了这个函数,它需要 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 个元素并进行比较。
解决方案
让我们从功能上思考,让类型引导我们完成这个过程。
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 格式打印日期,您始终可以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, 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
. 通过 Hoogle 的另一次旅行告诉我们,即使是该功能也可以在 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 相比),基于它的大小,它比乍一看更有用,并且捕获了很多更高的-order 编程模式非常优雅。
推荐阅读
- python - 在 DEAP 库中将列表类型转换为单个类型
- reactjs - 实现 react-ssr-adsense 包的问题
- c# - MStest 既找不到类也找不到命名空间
- python - 多处理池输出
- python - 我写的 GitHub Action 无权访问调用该操作的 repo 文件
- google-chrome-extension - Youtube 广告,书签视频 ID 和标题
- jq - 在没有选定数据的情况下以指示的格式获取输出
- python - 获取熊猫列的相对值
- mongodb - MongoDB Atlas:集群已暂停且身份验证失败
- weblogic - 在 weblogic wlst 中, cmo.set() 与 set()