为什么模式匹配中不允许[x,y,_]

时间:2019-08-16 00:04:43

标签: function haskell pattern-matching compiler-warnings clause

这是我的代码:

tell :: (Show a) => [a] -> String  
tell [] = "The list is empty"  
tell (x:[]) = "The list has one element: " ++ show x  
tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y  
tell (x:y:_) = "Other" 

为什么我不能将(x:y:_)更改为[x, y, _]

_的全局含义是什么?

4 个答案:

答案 0 :(得分:13)

  

最后一行为什么我无法将(x:y:_)更改为[x,y,_]

它们的含义不同:

  • [x, y, _]是指包含三个元素的列表;第一个元素绑定到x,第二个元素绑定到y,第三个元素未绑定到任何名称。等效于(x:y:_:[])
  • 相比之下,(x:y:_)指的是具有至少两个元素的列表,因为:是用于连接第一个元素的构造函数列表中的其余部分;在上述模式中,第一个元素绑定到x,第二个元素绑定到y,列表的其余部分(可以为空也可以不为空)没有任何名称。
  

_的全局含义是什么?

您实际上不应该在一个帖子中问两个问题,但这在这里很重要,因此:_只是一个占位符,当您想匹配特定的模式但不给它命名时。因此,在以上示例中:

  • [x,y,z]会将三元素列表的每个元素分别绑定到名称xyz(与(x:y:z:[])完全相同)。当您将z替换为_时,它仍然与三元素列表匹配,但没有为最后一个元素命名。
  • (x:y:z)将至少两个元素的列表的前两个元素绑定到名称xy,然后将列表的其余部分绑定到{{1 }}。当您将z替换为z时,它仍然与至少两个元素的列表匹配,但不会为列表的其余部分提供名称。

答案 1 :(得分:10)

您可以将(x : y : _)更改为[x, y, _],但这并不意味着相同。 [x, y, _]等效于(x : y : _ : []),即正好包含三个元素的列表(其中前两个元素绑定到xy)。

类似地,x : y是一个列表,其头(第一个元素)为x,而其尾部(其余元素)为y,但是[x, y]是一个正则列表两个要素。

我不确定您所说的“全局含义”是什么,但是_是通配符模式,可以匹配任何值,但不会将其绑定到名称。

答案 2 :(得分:3)

在模式中,_的意思是“在这里匹配任何内容,我不在乎”。

由于下划线是标识符中的有效字符,因此您几乎可以将_等同于您碰巧不在右侧使用的任何其他变量名。 (唯一的区别是,当您以相同的模式编写多个_时;在这种情况下,每个_被视为单独匹配,而在任何情况下都多次使用普通变量模式会给出有关变量定义冲突的错误)

由于在每个模式中仅使用单个_,所以我们可以看到,只要发明一个新变量并使用它,它就会做同样的事情。

所以tell (x:y:_) = ...tell (x:y:z) = ...相同。

OTOH tell [x, y, _] = ...tell [x, y, z] = ...相同。这与上述完全不同。在x:y:z中,每个:的左参数是列表中的,右参数是包含其余项的列表 1 。但是在[x, y, z]中,xyz中的每个都是列表中的项目

方括号列表语法[a, b, c, d, ...] 不允许您引用列表的结尾。每个逗号分隔的表达式都是列表中包含的项目,并且列表具有确切个您编写的元素数。您不能使用方括号列表语法来编写与具有任意数量元素的列表匹配的模式。

因此模式(x:y:_)(与(x:y:z))匹配的列表包含至少2个元素,其中_匹配的零或更多列表中前2个元素之后的其余元素。

模式[x, y, _]匹配一个具有 3个元素的列表,而_匹配一个第三个元素。


1 :运算符是右关联的,因此x:y:z的意思是x:(y:z)xy都是不同:的左引数,因此是项,而z:的右引数,因此是列表。


要回答有关“ _的全局含义”的问题,它实际上没有全局含义。它在不同的上下文中意味着不同的事物,这可能会有些混乱。它们通常都与“不知道”或“不在乎”的概念有关,但是它们之间并没有真正的联系。

上面的讨论是关于模式_的含义的。

表达式中,您可以使用_作为占位符来表示“我现在还不知道这是怎么回事”。如果您编译包含_作为表达式的代码,GHC会给您一个错误,但是它将尝试找出可以进入那里的类型,并在其中提供一些相关信息。错误消息。我经常通过写出一个充满_的基本“形状”来开始编写函数,然后让编译器告诉我我需要拿出什么样的东西来填补空白。

例如,编译它:

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

产生这个:

/tmp/foo.hs:4:18: error:
    • Found hole: _ :: b
      Where: ‘b’ is a rigid type variable bound by
               the type signature for:
                 mymap :: forall a b. (a -> b) -> [a] -> [b]
               at /tmp/foo.hs:2:1-31
    • In the first argument of ‘(:)’, namely ‘_’
      In the expression: _ : _
      In an equation for ‘mymap’: mymap f (x : xs) = _ : _
    • Relevant bindings include
        xs :: [a] (bound at /tmp/foo.hs:4:12)
        x :: a (bound at /tmp/foo.hs:4:10)
        f :: a -> b (bound at /tmp/foo.hs:4:7)
        mymap :: (a -> b) -> [a] -> [b] (bound at /tmp/foo.hs:3:1)
  |
4 | mymap f (x:xs) = _ : _
  |                  ^

/tmp/foo.hs:4:22: error:
    • Found hole: _ :: [b]
      Where: ‘b’ is a rigid type variable bound by
               the type signature for:
                 mymap :: forall a b. (a -> b) -> [a] -> [b]
               at /tmp/foo.hs:2:1-31
    • In the second argument of ‘(:)’, namely ‘_’
      In the expression: _ : _
      In an equation for ‘mymap’: mymap f (x : xs) = _ : _
    • Relevant bindings include
        xs :: [a] (bound at /tmp/foo.hs:4:12)
        x :: a (bound at /tmp/foo.hs:4:10)
        f :: a -> b (bound at /tmp/foo.hs:4:7)
        mymap :: (a -> b) -> [a] -> [b] (bound at /tmp/foo.hs:3:1)
      Valid hole fits include
        mempty :: forall a. Monoid a => a
          with mempty @[b]
          (imported from ‘Prelude’ at /tmp/foo.hs:1:1
           (and originally defined in ‘GHC.Base’))
  |
4 | mymap f (x:xs) = _ : _
  |                      ^

类型签名中还使用了_。这里的意思是“你告诉我,我不知道这是什么”。由于编译器大部分时间都可以推断类型,因此它只会在错误消息中告诉您应该使用什么来填补空白。

例如,编译它:

foo :: Int -> _
foo x = Just x

产生:

/tmp/foo.hs:2:15: error:
    • Found type wildcard ‘_’ standing for ‘Maybe Int’
      To use the inferred type, enable PartialTypeSignatures
    • In the type signature: foo :: Int -> _
  |
2 | foo :: Int -> _
  |               ^

(您甚至可以使用PartialTypeSignatures语言扩展名来允许GHC继续进​​行,并使用其推断的类型来填充空格,而不是将其视为错误)

答案 3 :(得分:0)

快速摘要:

data [a] = [] | a : [a]

因此,要进行模式匹配以自己解释:

head (x:_) = x

tail (_:xs) = xs

secondElem (_:x:_) = x

wrapInList x = [x]
wrapInList x = x : []
wrapTwoInlist x y = [x, y]
wraptwoInlist x y = x : y : []

unWrapFromList [x] = x

unWrapTwoFromList (x:y:_) = (x,y)

unWrapFromTwoSizeList [x,y] = (x,y)

unWrapFromThreeSizeList [x,y,_] = (x,y)

unWrapFromIDontKnowTheSizeList (x:y:_) = (x,y)

最后两个是[x,y,_](x:y:_)之间的主要区别,第一个是一个由三个元素组成的列表,另一个是您无法分辨的。