这个非常基本的函数声明有什么问题?

Raz*_*t4x 3 haskell

我是新手Haskell,我正在阅读" 了解你一个Haskell",并在页面中宣布了一个函数

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:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y 
Run Code Online (Sandbox Code Playgroud)

哪个工作正常.这本书说

这个函数是安全的,因为它处理空列表,单例列表,包含两个元素的列表和包含两个以上元素的列表.注意,(x:[])和(x:y:[])可以重写为[x]和[x,y](因为它的合成糖,我们不需要括号).我们不能用方括号重写(x:y:_),因为它匹配任何长度为2或更长的列表.

我尝试通过将最后一行改为来做到这一点

-- same as before
tell [x:y:_] = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y
Run Code Online (Sandbox Code Playgroud)

和哈斯克尔提出了一个非常丑陋的信息

    Could not deduce (a ~ [a0])
    from the context (Show a)
      bound by the type signature for tell :: Show a => [a] -> String
      at C:\Documents and Settings\Razor\Desktop\Other\baby.hs:(24,1)-(27,9
5)
      `a' is a rigid type variable bound by
          the type signature for tell :: Show a => [a] -> String
          at C:\Documents and Settings\Razor\Desktop\Other\baby.hs:24:1
    In the pattern: x : y : _
    In the pattern: [x : y : _]
    In an equation for `tell':
        tell [x : y : _]
          = "This list is long. The first two elements are: "
            ++ show x ++ " and " ++ show y
Failed, modules loaded: none.
Run Code Online (Sandbox Code Playgroud)

任何人都可以解释什么是错的吗?而作为每本书,我可以写(x:[])[x](我做到了,只是可以肯定的),但我为什么不能写tell (x:y:_)tell [x:y:_].而且我知道书给出了描述,但我真的无法理解这是什么问题?任何人都能用清楚的语言解释它吗?

Dan*_*her 9

[x:y:_]
Run Code Online (Sandbox Code Playgroud)

是一个模式,它匹配一个只有一个元素的列表,这是一个至少包含两个元素的列表.

模式可以嵌套,因此您可以使用例如foo (Just (x:xs)) = ...匹配Maybe [a]包装非空列表的值.嵌套模式可能需要括在括号中,但它们并不总是如此.在上面,我们可以使用括号(和空格)来强调模式的解释方式:

tell [ (x:y:_) ] = "This list ..." ...
Run Code Online (Sandbox Code Playgroud)

我们有顶级模式[ element ],element它本身就是x:y:_匹配具有至少两个元素的列表的模式.总而言之,模式匹配单元素列表,其元素是长度至少为2的列表.

因此,当您使用该模式时

tell [x:y:_] = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y
Run Code Online (Sandbox Code Playgroud)

编译器推断tell出列表列表作为参数,

tell :: (Show [b]) => [[b]] -> String
Run Code Online (Sandbox Code Playgroud)

但是你的签名

tell :: (Show a) => [a] -> String
Run Code Online (Sandbox Code Playgroud)

承诺tell适用于任何可用show元素列表,而不仅仅是列表列表.

推断类型和指定类型之间的不匹配是编译器抱怨的内容

    Could not deduce (a ~ [a0])
Run Code Online (Sandbox Code Playgroud)

(GHC选择命名类型变量a0,我选择b,这无关紧要).

符号[x]分别为 [x,y,z]是语法糖,列表的元素用逗号分隔,并且在逗号之间可以出现任意模式(例如,当[x,y,z]在模式上下文中使用时,表达式上下文中的表达式)x:y:_,但每个模式对应于单个元素的清单.这样的模式[x,y,z,w]只匹配具有与子模式一样多的元素的列表(并且每个元素必须与相应的子模式匹配).

另外,我不明白的是,为什么它允许(x:[])(x:y:[])被rewriten为[x][x,y]

这就是语法糖.通常,模式是

  • 文字,'a',"example"(这是语法糖的一种特殊情况,实际上), 3.4(这也是一种特殊的情况下,使用一个相等的比较==不同于通常的图案),
  • 一个通配符,_它匹配任何东西并且不绑定任何东西,
  • 标识符,name匹配任何内容并将相应的参数绑定到name,或
  • 一个充分的应用值构造,True,Just x(被施加的构造的参数本身的模式,所以-见上文- Just (x:xs)也是可能的)

(还有as-patterns list@(hd : tl)和lazy pattern ~pattern.)

列表构造函数是[](空列表)和(:)(通常说"cons",它构造一个元素的列表(它成为构造列表的头部)和另一个列表(它成为尾部),类型是(:) :: a -> [a] -> [a]),所以列表的构造函数模式

  • [] 对于空列表,和
  • x:xs对于非空列表,将传递列表的头部绑定到名称x,将尾部绑定到名称xs.

你可以嵌套(:)模式,例如

x : (y : (z : ws))
Run Code Online (Sandbox Code Playgroud)

并且,由于权限的相关性(:),您可以省略嵌套模式中的括号

x : y : z : ws
Run Code Online (Sandbox Code Playgroud)

对于列表,有一类进一步的模式,方括号之间的逗号分隔元素列表,

[x1, x2]
[x1, x2, x3, x4]
Run Code Online (Sandbox Code Playgroud)

依此类推,匹配列表与括号之间写的元素数量完全相同.这些被认为比相应的构造函数应用程序更容易

x1 : x2 : []
x1 : x2 : x3 : x4 : []
Run Code Online (Sandbox Code Playgroud)

两种形式的模式都是等价的(因此[x:y:_]也可以写成

(x:y:_) : []
Run Code Online (Sandbox Code Playgroud)

如果有人想).

我知道x:[]是捷径[x],但其他人呢?

反过来说,[x]是糖x : [],并且[x,y]是语法糖x : (y : []).以同样的方式,

[x:y:_]
Run Code Online (Sandbox Code Playgroud)

是语法糖

(x : (y : _)) : []
Run Code Online (Sandbox Code Playgroud)