2

有一些类型Record

type Day         = Integer
type Description = String
type Name        = String
type PhoneNumber = String
type Year        = Integer

data Month = January | February | March | April | May | June | July
           | August | September | October | November | December
           deriving (Eq, Ord, Enum, Show)
data Birthday = Birthday Month Day
  deriving (Eq, Show)
data DatingDate = DatingDate Year Month Day
  deriving (Eq, Show)

data Record = BirthdayRecord Name Birthday
            | PhoneRecord Name PhoneNumber
            | DatingRecord DatingDate Description
            deriving (Eq, Show)

以及按日期过滤这些记录的函数:

getAssignment :: (Year, Month, Day) -> [Record] -> [Record]
getAssignment (year, month, day) = filter matchDate
  where matchDate (BirthdayRecord _ (Birthday month day)) = True
        matchDate (DatingRecord (DatingDate year month day) _) = True
        matchDate _ = False

由于错误,此定义getAssignment不正确:

warning: Defined but not used: `year'

实际上,令我惊讶的是year,模式匹配部分getAssignmentyear模式匹配部分matchDate并不相同。

那么,year变量的范围界限在哪里开始和结束呢?它发生是因为where部分?

顺便说一句,这个错误可以通过一些冗余的大量使用(year, month, day)变量来避免。

getAssignment' :: (Year, Month, Day) -> [Record] -> [Record]
getAssignment' date = filter (matchDate date)
  where matchDate (_, m, d) (BirthdayRecord _ (Birthday month day)) =
          month == m && day == d
        matchDate (y, m, d) (DatingRecord (DatingDate year month day) _) =
          year == y && month == m && day == d
        matchDate _ _ = False

怎么可能改写?

4

2 回答 2

4

范围是整个表达式(包括 where 子句中的定义),除了模式中的变量总是定义一个新的变量绑定。

您应该在内部绑定中使用不同的变量名,而不是重用相同的名称。

getAssignment :: (Year, Month, Day) -> [Record] -> [Record]
getAssignment (year, month, day) = filter matchDate
  where matchDate (BirthdayRecord _ (Birthday month' day'))
           = month == month' && day == day'
        matchDate (DatingRecord (DatingDate year' month' day') _)
           = year == year' && month == month' && day == day'
        matchDate _ = False

重用变量名称以便它隐藏外部范围的变量称为遮蔽-Wall如果您使用(或-fwarn-name-shadowing仅启用此警告),GHC 应该在您执行此操作时警告您。

编辑:对于您的特定功能,这可能是一种更清晰的编写方式:

getAssignment :: (Year, Month, Day) -> [Record] -> [Record]
getAssignment (year, month, day) = filter matchDate
  where matchDate (BirthdayRecord _ birthday) = birthday == Birthday month day
        matchDate (DatingRecord date _)       = date == DatingDate year month day
        matchDate _                           = False

但是如果你想使用它,你不能避免给模式的一部分命名,即使只是为了将它与其他东西进行比较。

于 2012-04-07T19:53:00.690 回答
0

模式匹配具有以下形式:构造函数 binding1 binding2 ... 其中绑定仅(并且仅!)允许命名值的一部分以供使用是右手边。这就是你在左侧计算数值时所能做的。在您的第一个示例中,您似乎希望通过在绑定中引入绑定名称来限制匹配,但这不会起作用。看守卫你想要什么。

于 2012-04-07T20:21:09.630 回答