【问题标题】:How to understand and discriminate pattern match and tuple?如何理解和区分模式匹配和元组?
【发布时间】:2016-06-04 17:38:47
【问题描述】:

我是Haskell 的新手,对模式匹配和元组有点困惑。例如,在Learn You a Haskell for Great Good!:

您还可以使用 where 绑定来进行模式匹配!我们可以将之前函数的 where 部分重写为:

...  
where bmi = weight / height ^ 2  
      (skinny, normal, fat) = (18.5, 25.0, 30.0)  

(skinny, normal, fat) = (18.5, 25.0, 30.0) 是一个模式匹配,但它也是一个元组,对吧?如何理解和区分模式匹配和元组?

【问题讨论】:

  • 所有构造函数,不仅仅是元组的构造函数,都可以出现在模式中。您可以通过查看它们出现在= 符号(在函数中)的哪一侧来区分模式和表达式 - 左侧是模式,右侧是表达式。模式的语义可以在Haskell Report, s3.17 中找到。

标签: haskell


【解决方案1】:

在 Haskell 中,模式和表达式在很大程度上共享相同的内部语法,区别在于它们出现的位置。所以在你的例子中:

where bmi = weight / height ^ 2  
      (skinny, normal, fat) = (18.5, 25.0, 30.0)  

...(skinny, normal, fat) 是一个模式,因为它出现在等号的左侧,而(18.5, 25.0, 30.0) 是一个表达式,因为它出现在右侧等号的一侧。

在 Haskell 中有一个明确的上下文列表,其中允许使用模式,所以模式就是出现在这些上下文中的东西。第一个上下文:定义方程的左侧。这可以是顶级定义,也可以是 where 或 let 中的本地定义:

 -- Top level definition
 (pat1, pat2) = ...
   where
     -- Local definition in `where`
     (pat3:pat4:_) = let 
                        -- Local definition in `let`
                        Maybe pat5 = ...
                     in ...

第二个上下文:-> 的左侧在 case 表达式中:

case expr of
  Just pat -> 2*a
  Nothing -> 42

第三个上下文:<- 在do-notation 或列表推导中的左侧:

example1 = do
  (pat1, pat2) <- ...
  ...

example2 = [f x y | (x, y) <- ...]

我可能会遗漏一些其他的上下文,但至少这些是主要的。


这里的另一个重要概念是 数据构造函数:在 Haskell 中提供两个功能的常量或运算符:

  1. 作为一个表达式,可用于构造其类型的值;
  2. 作为一种模式,可用于解构其类型的值。

再一次,数据构造函数的特定使用是前者还是后者完全取决于它是“左侧”还是“右侧”使用。

【讨论】:

    【解决方案2】:

    (以下内容并不完全正确;正如 Shumush 指出的那样,模式匹配只能使用数据构造函数完成,而不仅仅是任何函数。下面提到的(:) 和 Just 是实际的数据构造函数;各种逗号函数不' 似乎不是,所以我实际上并不知道与元组匹配的模式是如何工作的。也许可以认为这是对元组模式匹配的巧合类比。)

    首先,考虑模式匹配如何应用于列表,以map 的定义为例。

    map :: (a -> b) -> [a] -> [b]
    map f [] = []
    map f (x:xs) = f x (map f xs)
    

    在这里,您可以对列表进行模式匹配,因为(:) 是一个从一个元素和另一个列表生成列表的函数。也就是说,(x:xs) 可以通过将 1 绑定到 x 和 [2,3] 到 xs 来匹配 [1,2,3],因为 (:) 1 [2, 3] = 1 : [2,3] = [1,2,3]。

    (,) 是一个产生元组的函数:

    > :t (,)
    (,) :: a -> b -> (a, b)
    

    所以(,) 1 2 = (1, 2)。因此,您可以像使用 (:) 一样使用 (,) 进行模式匹配:

    (skinny, normal, fat) = (18.5, 25.0, 30.0)
    

    (Prelude 提供了(,,) :: a -&gt; b -&gt; c -&gt; (a, b, c)、(,,,) 等的定义。)

    将 18.5 绑定到 skinny,将 25.0 绑定到 normal,并将 30.0 绑定到 fat,因为

    (,) 18.5 25.0 30.0 = (18.5, 25.0, 30.0)
    

    将模式匹配视为函数评估的反面。您无需将函数应用于值以生成新值,而是“分解”一个值以获取用于生成该值的操作数。

    模式匹配可以通过 any 函数调用来完成,这就是为什么你可以在 Maybe 值之类的东西上进行模式匹配

    -- From the Functor instance of Maybe
    fmap :: (a -> b) -> (Maybe a) -> (Maybe b)
    fmap f Nothing = Nothing
    fmap f (Just x) = f x
    

    由于Just :: a -&gt; Maybe a 是一个函数(特别是数据构造函数),您可以通过将Just 创建的Maybe 中包装的值绑定到x 来进行模式匹配。

    【讨论】:

    • 但是(:) 和(,) 不是函数。函数不能用于模式匹配,只有构造函数可以,因为它们“构造数据”而不“计算值”。
    • 是的,它们是函数。例如,在 GHCI 中键入 :t (:) 以查看其类型。中缀运算符只是名称仅由符号组成的函数。通过将常规函数包含在反引号中可以将它们视为中缀运算符(fmap f [1,2] 与 f `fmap` [1,2] 相同),通过将中缀运算符括在括号中可以将它们视为常规函数。
    • Just 是一个构造函数,但如果你输入:t Just,你将得到Just :: a -&gt; Maybe a。构造函数可以定义为中缀运算符。您可以定义这样的列表data List a = [] | a : List a。构造函数是“类似”函数,因为它们接受一些值并“计算”另一个值,但只有构造函数可以用于模式匹配。您不能使用 mktuple a b = (a, b) 进行模式匹配。 a mktuple` b = (1, 2)` 无效
    • 是的,我忘记了规则,不过(我认为它们必须以 : 开头?), 有点奇怪,因为 (,,) 不是一个中缀运算符作为三元运算符。你写的是(,,) 1 2 3 或(1, 2, 3),而不是1 ,, 2 ,, 3,所以我认为可能在, 的情况下,模式匹配是一种特殊情况,而不是它作为构造函数的直接结果。虽然Just 是一个实际的数据构造函数(data Maybe a = Just a | Nothing),但我认为(,) 和family 只是看起来 像构造函数的函数。我看看能不能找到各种元组类型的实际定义。
    • 在this post C. A. McCann 解释说元组构造函数(以这种方式使用:(1,2,3))是语法糖,因此不能实现为Maybe 或其他数据类型。
    【解决方案3】:

    模式匹配用于命名以特定模式设置的变量。 在这里,你可以说“变量skinnynormal和fat代表一个等于(18.5, 25.0, 30.0)的元组的值”

    您可以在其他情况下使用模式匹配,例如列表。

    first_element [] = error "Empty list"
    first_element (x:rest) = x
    

    这里的模式要么是一个空列表[],要么是一个值x,后跟给定列表的其余部分。

    如果你输入 GHCI

    let (x:xs) = [2, 3, 4]
    

    然后通过模式匹配x 将具有值2 和xs 值[3, 4]。

    元组只是“盒子”,可以包含多种类型的多个值。 一个点可以表示为(Double, Double) 类型的元组。

    您可以构造任何类型的任何元组。在您的示例中,(18.5, 25.0, 30.0) 的类型可能为 (Double, Double, Double)

    【讨论】:

      猜你喜欢
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      • 2017-10-15
      • 2021-12-02
      • 2018-10-04
      • 1970-01-01
      • 1970-01-01
      • 2018-12-01
      相关资源
      最近更新 更多