模式Haskell中的解析错误 - x:y:z

时间:2018-03-04 22:01:40

标签: haskell

我正在进行练习测验,其中一个问题可以让你使用

data Person = MakePerson String Int Float 

其中String是名称,Int是年龄,Float是高度。我必须创建一个函数,该函数接收一个人列表并返回最老的人(如果有n个最老的人知道严格的规则,只需选择其中一个。 我写了这个

oldest :: [Person] -> String
oldest [] = error("Empty list")
oldest ((MakePerson a b c):(MakePerson d e f):g)
    | (b >= e) && (g == []) = a
    | (b >= e) = oldest (MakePerson a b c):g
    | (b < e) && (g == []) = d
    | otherwise = oldest (MakePerson d e f):g

但是,我得到一个解析错误。我不知道如何在没有这种双重模式的情况下创建这个功能,因为我需要总是比较b和e而我没有其他方式来访问他们的年龄。我怎样才能为我的参数写出这个模式?

模式中的解析错误:最旧 最古老的(MakePerson a b c):( MakePerson d e f):g

3 个答案:

答案 0 :(得分:3)

要做的一件更简单的事情是定义一个按年龄比较两个人的函数,然后使用Data.List.maximumBy来获取最老的。

compareByAge :: Person -> Person -> Ordering
compareByAge (MakePerson _ a1 _) (MakePerson _ a2 _) = compare a1 a2

getName :: Person -> String
getName (MakePerson name _ _) = name

oldest :: [Person] -> String
oldest people = (getName . maximumBy compareByAge) people
-- or just oldest =  getName . maximumBy compareByAge

尽可能避免使用显式递归函数,因为通常有一个预定义函数可用于抽象必要的递归。

答案 1 :(得分:3)

记录语法将使您的生活更轻松。查看relevant LYAH chapter

import Data.List(maximumBy)
import Data.Function(on)

data Person = MakePerson {getName :: String, getAge :: Int, getHeight :: Float}
oldest = getName . maximumBy (compare `on` getHeight)

然后:

Prelude Data.List> l = [(MakePerson "Alice" 30 5.90), (MakePerson "Bob" 40 5.55), (MakePerson "Claire" 25 6.05)]
Prelude Data.List> oldest l
"Claire"

修改

Updated the comparison function per @JonPurdy's comment.

答案 2 :(得分:1)

我假设您的策略是比较列表的前两个元素,然后递归列表的其余部分。那个策略不起作用。以下面的列表为例(我只会将年龄写为列表的元素以简化事情):

23:25:21

如果您对此列表进行模式匹配,您将获得第二种情况,即23:25:g,其中g是列表的其余部分。您比较23和25,您会看到25是较旧的,然后将其与列表其余部分g进行比较。但是g现在只有一个元素。它不是一个空列表,它不是包含两个Person类型元素的列表。所以程序会失败。

相反,您必须以更简单,更纯粹的递归方式处理此问题。如果列表为空,则引发并出错。如果列表只有1个人,则返回该人员的年龄,因为默认情况下它们是最早的。如果列表是其他任何内容,请将列表年龄的头部与列表其余部分的最旧人员进行比较。这样,该函数将逐个挑选PersonS,并将它们进行比较以获得最旧的PersonS。