定义我自己的连接

时间:2018-06-23 00:44:01

标签: haskell

我正在读一本书,它要求我定义自己的concat函数。我在这里正确定义了它:

concat :: [[a]] -> [a]
concat [] = []
-- concat [[]] = []
concat (xs:xss) = xs ++ (Main.concat xss)

对此我有两个问题。

  1. 为什么我不需要我注释掉的那一行?
  2. 当我逐步使用Main.concat [[]]进行调用时,如何对其进行评估?我的想法是,它进入了第二个定义,但我无法理解。如果我是对的并且转到第二个定义,那么xsxss的值是什么?

3 个答案:

答案 0 :(得分:5)

  

如果我是对的,那么转到第二个定义,xsxss的值是什么?

让我们问:

> f (xs:xss) = (xs, xss)
> f [[]]
([],[])

因此,现在尝试将这些值替换为您的定义:

concat (xs:xss) = xs ++ (Main.concat xss)
                = [] ++ (Main.concat [] )
                = [] ++ []
                = []

答案 1 :(得分:3)

[[]]也可以写为[] : [](使用(:)构造函数),即,其头是空列表,而尾是空列表的列表。这样就可以成功对xs:xss进行模式匹配。

因此,在将xs[]xss[]匹配之后,我们实际上得到了以下行为:

let xs = [] in
  let xss = [] in
    xs ++ (Main.concat xss)

通过替换,我们得到

[] ++ (Main.concat [])

该递归调用将达到基本情况,并返回[],得到[] ++ [],最终得到[]

答案 2 :(得分:1)

TL / DR: [[]] = [] : [][1] = 1 : []一样,因此它与x:xs模式匹配。

在Haskell中,列表的定义如下:

data [a]    -- a list of `a`s is...
  = []      -- an empty list...
  | a : [a] -- or an `a`, then a list of `a`s.

当我们写[1,2,3]时,这只是1 : (2 : (3 : []))的语法糖,由于:是我们所说的 right-associative ,所以我们也可以写1:2:3:[]

因此,列表上的模式匹配通常只需要两种情况:[](x:xs)[]显然是空列表,(x:xs)是非空列表,其中x是列表的第一个元素,xs是列表的其余元素。

让我们检查该定义,而忽略注释行。顺便说一下,我删除了一些不必要的括号:

concat :: [[a]] -> [a]
concat []     = []                  -- The empty list case
concat (x:xs) = x ++ Main.concat xs -- The nonempty list case

我们看到您实际上已经涵盖了所有情况:现在已为所有列表定义了这种情况,因此至少在理论上,案例concat [[]] = []是多余的。

实际上也是这样,因为无论如何concat [[]] = [] ++ concat [] = []属于第二行的情况,因为[[]] = [] : [](x:xs)模式匹配。