Haskell 如何跨模式匹配执行where子句

Haskell 如何跨模式匹配执行where子句,haskell,Haskell,我可以得到一个where子句来跨模式工作,该子句与case语句匹配: updateHung :: Text -> Maybe Char -> Int updateHung word got = let x = case got of Just l | elem l . unpack $ word -> pass | otherwise -> strike

我可以得到一个
where
子句来跨模式工作,该子句与
case
语句匹配:

updateHung :: Text -> Maybe Char -> Int
updateHung word got =
    let x = case got of
            Just l
                | elem l . unpack $ word -> pass
                | otherwise -> strike
            Nothing -> pass
            where
                strike = 1
                pass = 0
    in x
但当我尝试使用多部分函数做同样的事情时,它不起作用:

updateHung :: Text -> Maybe Char -> Int
updateHung word (Just l)
    | elem l . unpack $ word = pass
    | otherwise = strike
updateHung word Nothing = pass
    where
        strike = 1
        pass = 0

有什么方法可以让它工作吗?

在工作版本中,您的
where
子句缩进错误。您将其缩进,就像它附加到
case
语句中一样,但实际上它附加到
x
的定义中,并且缩进得更清楚

updateHung :: Text -> Maybe Char -> Int
updateHung word got =
    let x = case got of
          Just l
              | elem l . unpack $ word -> pass
              | otherwise -> strike
          Nothing -> pass
          where
            strike = 1
            pass = 0
    in x
where
子句的作用域始终为单个模式,跨越该模式的所有保护。这非常重要,因为这允许它使用模式引入的变量。例如,在
pass
的定义中使用
l
可能很有用,但如果您可以将其范围限定到整个
case
语句,则这将毫无意义

如果希望所有模式的范围内都有变量,则必须在开始模式匹配之前绑定这些变量。使用
let
where
为函数定义一个等式,并在其中定义变量,然后在
情况下对所有参数的元组或您关心的参数执行其余逻辑:

updateHung :: a -> Maybe Char -> Int
updateHung word got =
  let strike = 1
      pass = 0
  in case got of
     Just l
       | elem l . unpack $ word -> pass
       | otherwise -> strike
     Nothing -> pass

解释如何以及为什么将
where
子句的范围限定为单个等式,而不是整个定义,并使用
case
提供替代方法。但是,为了回答更狭义的问题,如果您想继续使用等式样式,但在
where
子句中也有共享的局部定义,您可以使用作用域为相同
where
块的局部“worker”函数作为辅助定义,因此它们都在范围内:

updateHung = go
  where
    go word (Just l)
      | elem l $ unpack word = pass
      | otherwise = strike
    go word Nothing = pass

    strike = 1
    pass = 0
然后,作为简化,您可以将
word
参数提升到
updateHung
,因为它在
go
中总是以相同的方式匹配:

updateHung word = go
  where
    go (Just l)
      | elem l $ unpack word = pass
      | otherwise = strike
    go Nothing = pass

    strike = 1
    pass = 0
当局部定义是递归的,而外部定义只是设置其初始参数时,这通常称为,这在两个方面有利于性能:

  • 共享像
    word
    这样的参数,这些参数在整个内部计算过程中保持不变,而不是重复地将它们作为参数传递给递归调用

  • 允许外部定义内联以暴露更多优化(因为GHC通常不会内联递归函数)

  • updateHung word = go
      where
        go (Just l)
          | elem l $ unpack word = pass
          | otherwise = strike
        go Nothing = pass
    
        strike = 1
        pass = 0