模式匹配变量的范围

时间:2012-04-07 19:32:56

标签: haskell pattern-matching scope

有一些类型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

如何改写?

2 个答案:

答案 0 :(得分: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

重用变量名以使其隐藏外部作用域中的变量称为 shadowing 。如果您使用-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

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

答案 1 :(得分:0)

模式匹配的形式如下:构造函数binding1 binding2 ...其中绑定只是(并且只有!)允许为使用值命名的一部分是右侧。这就是你在左边的价值时所能做的一切。 在您的第一个示例中,您似乎希望通过在绑定中引入绑定名称来约束匹配,但这不会以这种方式工作。看看你想要什么。