我正在读一本书,它要求我定义自己的concat
函数。我在这里正确定义了它:
concat :: [[a]] -> [a]
concat [] = []
-- concat [[]] = []
concat (xs:xss) = xs ++ (Main.concat xss)
对此我有两个问题。
Main.concat [[]]
进行调用时,如何对其进行评估?我的想法是,它进入了第二个定义,但我无法理解。如果我是对的并且转到第二个定义,那么xs
和xss
的值是什么?答案 0 :(得分:5)
如果我是对的,那么转到第二个定义,
xs
和xss
的值是什么?
让我们问:
> 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)
模式匹配。