Avoiding do statement in foldM
1 2 3 4
| g ll =
foldlM (\\ some_list b -> do
part <- f b
return (some_list ++ part )) [] ll |
在上面的代码中,我使用 do statement 只是因为 f 函数返回一个单子类型: M a 其中 a 是一个列表。
(我用 <-"解压"该列表。这就是我需要 do statement 的原因)。我可以避免它并写得更简洁吗? (是的,我知道我可以使用 >>= 编写它,但我也考虑使用更好的方法。)
- 你想要 fmap :: Functor f => (a -> b) -> f a -> f b。
-
g 乍一看是 fmap concat (mapM f ll)
-
像这样:g = foldlM (\\some_list b -> (some_list++) <$> f b) []
foldlM 是适合这项工作的错误工具。正如 chepner 的回答所示,您可以使用它,但是连接列表的方式可能会变得昂贵。 Luka Rahne 的单线更好:
另一种选择是直接使用foldr:
1
| g = foldr (\\x r -> (++) <$> f x <*> r ) (pure []) |
另一种编写第二个版本的方法,通过内联 foldr:
1 2
| g [] = pure []
g (x : xs) = (++) <$> f x <*> g xs |
-
有什么区别(在你把 some_list ++ part 变成 part ++ some_list 之后)?在我看来,这三个版本都是等效的,而且它们都泄漏了。 g 可以更有效地定义为除 [] 以外的幺半群(例如 Sum),但我认为没有一个版本可以懒惰地生成列表,因此不会泄漏。所以当涉及到大列表时,它只是列表是一个错误的工具。
-
@user3237465,如果你正在处理一个懒惰的 Applicative,你可以摆脱它。否则,是的,它们都会泄漏空间。我正在解决这样一个事实,即无论上下文如何,严重关联的 ++ 也会导致时间复杂性问题。
-
@dfeuer 谢谢! :) 第一个版本可以理解,但第二个我无法理解
你的 do 表达式
1 2 3
| do
part <- f b
return (some_list ++ part ) |
遵循 fmap 捕获的提取-应用-返回模式(由于身份 fmap f k = k >>= return . f
您从计算 f b 中提取 part
您将 (some_list ++) 应用于 part
您返回该应用程序的结果。
这可以通过 fmap:
一步完成
1 2
| -- foldlM (f b >>= return . (some_list ++)) [] ll
foldlM (\\some_list b -> fmap (some_list ++) (f b )) [] ll |
- some_list 是折叠的一部分。我认为 Luka Rahne 击中了头。
-
是的,我摆脱了整个函数,而不仅仅是 do 表达式。那是固定的。
-
二次复杂度仍然很不幸。