[英]Implementing List#flatten in Haskell
Scala offers a List#flatten
method for going from List[Option[A]]
to List[A]
. 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)
I attempted to implement it in Haskell: 我试图在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
However I don't like the fact that g
is a partial, ie non-total, function. 但是,我不喜欢
g
是部分(即非总体)函数的事实。
Is there a total way to write such flatten
function? 是否有完整的方法来编写这样的
flatten
函数?
Your flatten
is the same as catMaybes
(link) which is defined like this: 你的
flatten
与catMaybes
(link)相同,其定义如下:
catMaybes :: [Maybe a] -> [a]
catMaybes ls = [x | Just x <- ls]
The special syntax Just x <- ls
in a list comprehension means to draw an element from ls
and discard it if it is not a Just
. 列表理解中的特殊语法
Just x <- ls
意味着从ls
绘制一个元素,如果它不是Just
,则丢弃它。 Otherwise assign x
by pattern matching the value against Just x
. 否则,分配
x
通过模式匹配对价值Just x
。
A slight modification of the code you have will do the trick: 稍微修改一下您所拥有的代码即可:
flatten :: [Maybe a] -> [a]
flatten xs = xs >>= f
f :: Maybe a -> [a]
f x = case x of Just j -> [j]
Nothing -> []
If we extract the value inside of the Just
constructor in f
, we avoid g
altogether. 如果我们在
f
提取Just
构造函数内部的值,我们完全避免g
。
Incidentally, f
already exists as maybeToList
and flatten
is called catMaybes
, both in Data.Maybe
. 顺便说一句,
f
已经存在了maybeToList
和flatten
被称为catMaybes
,无论是在Data.Maybe
。
One could quite easily write a simple recursive function which goes through a list and rejects all the Nothing
s from the Maybe monad. 人们可以很容易地写一个简单的递归函数,它通过一个列表并拒绝所有来自Maybe monad的
Nothing
。 Here's how I'd do it as a recursive sequence: 这是我如何做一个递归序列:
flatten :: [Maybe a] -> [a]
flatten [] = []
flatten (Nothing : xs) = flatten xs
flatten (Just x : xs) = x : flatten xs
However, it may be clearer to write it as a fold: 但是,将它写成折叠可能更清楚:
flatten :: [Maybe a] -> [a]
flatten = foldr go []
where go Nothing xs = xs
go (Just x) xs = x : xs
Or, we could use a blindingly elegant solution thanks to @user2407038, which I'd recommend playing around with in GHCi to work out the individual functions' jobs: 或者,由于@ user2407038,我们可以使用一个非常优雅的解决方案,我建议在GHCi中使用它来计算各个功能的工作:
flatten :: [Maybe a] -> [a]
flatten = (=<<) (maybe [] (:[])
And it's faster, folded brother: 这是更快,折叠的兄弟:
flatten :: [Maybe a] -> [a]
flatten = foldr (maybe id (:))
Your solution is halfway there. 你的解决方案就在那里。 My suggestion if to rewrite your function
f
to use pattern matching (like my temporary go
function), and enclose it in a where
statement to keep relevant functions in one place. 我建议如果要重写你的函数
f
来使用模式匹配(比如我的临时go
函数),并将它包含在where
语句中以将相关函数保存在一个地方。 You've got to remember the differences in function syntax within scala and Haskell. 你必须记住scala和Haskell中函数语法的差异。
The big problem you're having is you don't know the differences I've mentioned. 你遇到的一个大问题是你不知道我提到的差异。 Your
g
function can use pattern matching with multiple patterns: 你的
g
函数可以使用模式匹配多个模式:
g :: Maybe a -> [a]
g (Just x) = [x]
g Nothing = []
There you go: your g
function is now what you call 'complete', though more accurately, it would be said to have exhaustive patterns . 你去了:你的
g
函数现在称为'完整',但更准确地说,它可以说是详尽的模式 。
You can find more about function syntax here . 您可以在此处找到有关函数语法的更多信
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.