StackOverflow的人们好!
我已经开始使用Haskell了,偶然发现了concatMap
函数。
由于我对这种语言很陌生,因此我在理解以下代码时遇到了一些问题(source):
concatMap f = cmap where
cmap [] = []
cmap (x : xs) = accum (f x) where
accum [] = cmap xs
accum (y : ys) = y : accum ys
据我所知,函数concatMap
接受一个函数f
。
但是我们如何设置一个与另一个相等的功能呢?我们是否将f
的结果设为cmap
,或者我们是否使用cmap
作为f
的参数?
非常感谢任何帮助,
提前谢谢!
答案 0 :(得分:4)
您发布的代码对初学者来说并不容易。幸运的是,我们可以一点一点地重写它:
accum [] = cmap xs
accum (y : ys) = y : accum ys
上面的函数,当输入列表为空时返回cmap xs
,否则在y:ys
上发出y
作为第一个输出元素,然后以递归方式继续输出accum xs
因此,accum zs
只会输出zs
中的所有元素,然后继续cmap xs
。我们可以将其重写为:
accum zs = zs ++ cmap xs
其中++
是列表连接。
然后我们可以按如下方式重写整个代码:
concatMap f = cmap where
cmap [] = []
cmap (x : xs) = f x ++ cmap xs
我们可以进一步改写为
concatMap f [] = []
concatMap f (x : xs) = f x ++ concatMap f xs
对于初学者来说应该更容易访问。更为非正式地,上述定义满足等式:
concatMap f [x1,x2,...,xn] =
f x1 ++ f x2 ++ ... ++ f xn ++ []
因此,我们可以看到concatMap
的作用。它将f
应用于每个列表元素,并且每个列表元素f
必须返回一个列表。然后,连接所有这些列表。
例如:
concatMap (\x -> [1..x]) [3,1,2] =
[1,2,3] ++ [1] ++ [1,2] =
[1,2,3,1,1,2]
答案 1 :(得分:3)
chi的答案涵盖了concatMap
如何运作,因此我将专注于您的一个疑问:
但是我们如何设置一个与另一个函数相等的函数呢?
Haskell中的函数是一个值,就像任何其他函数一样。
GHCi> foo = "foo"
这是foo
的定义,恰好是一个字符串:
GHCi> :t foo
foo :: [Char]
GHCi> putStrLn foo
foo
以同样的方式......
GHCi> add = (+)
......这是add
的定义,恰好是一个函数:
GHCi> :t add
add :: Num a => a -> a -> a
GHCi> add 2 3
5
在上面的定义中,为了强调我只是定义了一个值,我没有明确地写出add
的任何一个参数。但这样做完全没问题:
GHCi> add x y = (+) x y
(请注意,x + y
,这就是我们通常编写上述定义右侧的方式,只是(+) x y
的方便替代语法。)
编写定义的另一种方式是利用部分应用程序来仅提及第一个参数:
GHCi> add x = (+) x
我们也可以,为了它(并且可能澄清正在发生的事情),将(+) x
移动到where子句中的单独定义......
GHCi> :{
GHCi| add x = plusX
GHCi| where
GHCi| plusX = (+) x
GHCi| :}
...并再次明确地写下第二个参数:
GHCi> :{
GHCi| add x = plusX
GHCi| where
GHCi| plusX y = (+) x y
GHCi| :}
plusX
是一个函数,它接受一个参数并将其添加到x
。 add
是一个函数,它接受一个参数x
,并返回与plusX
对应的x
函数。这是第二个函数,当我们执行add
时,它将第二个参数带到add 2 3
(顺便说一下,它等同于(add 2) 3
)。
现在,将上述内容与您的concatMap
定义进行比较:
concatMap f = cmap where
cmap [] = []
cmap (x : xs) = accum (f x) where
accum [] = cmap xs
accum (y : ys) = y : accum ys
定义以类似的方式列出。 cmap
是一个函数,它使用一个列表并使用f
(concatMap
的参数)从中生成结果,就像plusX
使用x
一样1}} add
的参数。
所以,总结一下:
我们将f的结果设置为等于cmap,还是我们使用cmap作为f的参数?
都不是。我们使用f
来定义cmap
,然后将其用于定义concatMap
整体。