在 Haskell 中实施 List#flatten

Implementing List#flatten in Haskell

Scala 提供了一个 List#flatten 方法,用于从 List[Option[A]]List[A]

scala> val list = List(Some(10), None)
list: List[Option[Int]] = List(Some(10), None)

scala> list.flatten
res11: List[Int] = List(10)

我试图在 Haskell 中实现它:

flatten :: [Maybe a] -> [a]
flatten xs = map g $ xs >>= f

f :: Maybe a -> [Maybe a]
f x = case x of Just _  -> [x]
                Nothing -> []

-- partial function!
g :: Maybe a -> a
g (Just x) = x

但是我不喜欢 g 是部分函数,​​即非全部函数。

是否有的方法来编写这样的flatten函数?

您的 flattencatMaybes (link) 相同,其定义如下:

catMaybes :: [Maybe a] -> [a]
catMaybes ls = [x | Just x <- ls]

列表理解中的特殊语法Just x <- ls表示从ls中提取一个元素,如果不是Just则丢弃它。否则通过将值与 Just x.

进行模式匹配来分配 x

对您的代码稍作修改即可解决问题:

flatten :: [Maybe a] -> [a]
flatten xs = xs >>= f

f :: Maybe a -> [a]
f x = case x of Just j  -> [j]
                Nothing -> []

如果我们在 f 中提取 Just 构造函数内部的值,我们将完全避免 g

顺便说一句,f 已经作为 maybeToList and flatten is called catMaybes 存在,都在 Data.Maybe.

可以很容易地编写一个简单的递归函数,它遍历一个列表并拒绝来自 Maybe monad 的所有 Nothing。以下是我如何将其作为递归序列执行:

flatten :: [Maybe a] -> [a]
flatten [] = []
flatten (Nothing : xs) =     flatten xs
flatten (Just x  : xs) = x : flatten xs

不过写成折叠可能更清楚:

flatten :: [Maybe a] -> [a]
flatten = foldr go []
    where go  Nothing xs =     xs
          go (Just x) xs = x : xs

或者,感谢@user2407038,我们可以使用一个非常优雅的解决方案,我建议在 GHCi 中使用它来计算各个函数的工作:

flatten :: [Maybe a] -> [a]
flatten = (=<<) (maybe [] (:[])

而且速度更快,折叠哥:

flatten :: [Maybe a] -> [a]
flatten = foldr (maybe id (:))

您的解决方案已完成一半。我的建议是重写你的函数 f 以使用模式匹配(就像我的临时 go 函数),并将它包含在 where 语句中以将相关函数放在一个地方。您必须记住 scala 和 Haskell.

中函数语法的差异

你遇到的最大问题是你不知道我提到的区别。您的 g 函数可以对多个模式使用模式匹配:

g :: Maybe a -> [a]
g (Just x) = [x]
g  Nothing = []

好了:您的 g 函数现在就是您所说的 'complete',但更准确地说,它应该具有 穷举模式

您可以找到有关函数语法的更多信息 here