使用List Monad vs fmap

时间:2017-09-09 17:29:30

标签: haskell monads

是否有任何实际用途的列表monad不会只是滚动到fmap?你什么时候使用list over fmap和list monad?

例如,您可以执行[1,2,3] >>= return . ( + 1),但这与(+1) <$> [1,2,3]相同 - 何时使用bind而不返回列表?

3 个答案:

答案 0 :(得分:12)

使用bind with return相当于使用fmap。事实上,

fmap f m = m >>= return . f

无法用fmap复制的bind的用法正是那些不涉及这种使用返回的用法。为列表提供一个(希望)有趣的例子,让我们谈谈L-Systems

L系统由Aristid Lindenmeyer于1968年创建。作为重写系统,它们以一个简单的对象开始,并使用一组重写规则制作重复替换它的一部分。它们可用于生成分形和其他自相似图像。无上下文,确定性L系统(或D0L)由字母表,公理和生产规则集合的三重定义。

对于我们的字母表,我们将定义一个类型:

data AB = A | B deriving Show

对于我们的公理或起始状态,我们会使用[A, B]这个词。

myAxiom = [A, B]

对于我们的规则,我们需要一个从单个字母到一系列字母的地图。这是AB -> [AB]类型的函数。让我们使用这条规则:

myRule :: AB -> [AB]
myRule A = [A, B]
myRule B = [A]

要应用规则,我们必须使用其生产规则重写每个字母。我们必须同时为单词中的所有字母执行此操作。方便的是,这正是>>=对列表的作用:

apply rule axiom = axiom >>= rule

现在,让我们将规则应用于我们的公理,从而产生L系统的第一步:

> apply myRule myAxiom
> [A, B, A]

这是Lindenmeyer的original L-System,用于模拟藻类。我们可以迭代看它的进展:

> mapM_ print . take 7 $ iterate (>>= myRule) myAxiom
[A,B]
[A,B,A]
[A,B,A,A,B]
[A,B,A,A,B,A,B,A]
[A,B,A,A,B,A,B,A,A,B,A,A,B]
[A,B,A,A,B,A,B,A,A,B,A,A,B,A,B,A,A,B,A,B,A]
[A,B,A,A,B,A,B,A,A,B,A,A,B,A,B,A,A,B,A,B,A,A,B,A,A,B,A,B,A,A,B,A,A,B]

通常,对于列表的绑定是concatMap,并且当您想要将映射与串联组合时,可以精确地使用它。另一种解释是列表表示非确定性选择,并且通过从列表中选择一次可能性来绑定函数。例如,滚动骰子:

do
  d1 <- [1..6]
  d2 <- [1..6]
  return (d1, d2)

这提供了滚动2d6的所有可能方法。

答案 1 :(得分:1)

factors :: Int -> [Int]
factors n = do
    q <- [1..n]
    filter ((==n) . (*q)) [1..n]

......或者,在贬低的符号中,

factors n = [1..n] >>= ($[1..n]) . filter . fmap (==n) . (*)

这当然难以有效,但它确实有效:

*Main> factors 17
[17,1]
*Main> factors 24
[24,12,8,6,4,3,2,1]
*Main> factors 34
[34,17,2,1]

对于那些不像*这么简单的操作,所以你无法避免像这样的蛮力方法,这实际上可能是一个很好的解决方案。

答案 2 :(得分:1)

首先,concatMap只是(=<<)。而concat只是join。我在实际代码中经常使用这两种方法。

您可以做的另一件事是将函数列表应用于一个值。

λ:> applyList = sequence
λ:> applyList [(2*), (3+)] 4
[8,7]

您还可以生成列表中所有子集的列表

λ:> import Control.Monad
λ:> allSubsets = filterM (const [True, False])
λ:> allSubsets "ego"
["ego","eg","eo","e","go","g","o",""]

甚至可以枚举所有可以用字母组成的字符串

λ:> import Data.List
λ:> import Control.Monad
λ:> allStrings = sequence <=< (inits . repeat)
λ:> take 100 $ allStrings ['a'..'z']
["","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","aa","ab","ac","ad","ae","af","ag","ah","ai","aj","ak","al","am","an","ao","ap","aq","ar","as","at","au","av","aw","ax","ay","az","ba","bb","bc","bd","be","bf","bg","bh","bi","bj","bk","bl","bm","bn","bo","bp","bq","br","bs","bt","bu","bv","bw","bx","by","bz","ca","cb","cc","cd","ce","cf","cg","ch","ci","cj","ck","cl","cm","cn","co","cp","cq","cr","cs","ct","cu"]

也许更实际的是,您可以使用应用实例将两个列表组合在一起

λ:> zipWith' f xs ys = f <$> xs <*> ys
λ:> zipWith' (+) [1..3] [5..8]
[6,7,8,9,7,8,9,10,8,9,10,11]