在 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
函数?
您的 flatten
与 catMaybes
(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。
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
函数?
您的 flatten
与 catMaybes
(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。