【问题标题】:Use of List Monad vs fmapList Monad 与 fmap 的使用
【发布时间】:2017-09-09 17:29:30
【问题描述】:

list monad 有什么实际用途,不只是滚动到 fmap 吗?你什么时候会在 fmap 上使用 bind 和 list monad?

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

【问题讨论】:

  • fmap 要求结果中的元素与原始列表一样多; monad 允许您从(通过guard)中删除或添加到(我假设;空白如何)结果列表。

标签: haskell monads


【解决方案1】:

使用带返回的绑定等同于使用 fmap。确实,

fmap f m = m >>= return . f

无法用 fmap 复制的 bind 的使用正是那些不涉及 return 的使用。为了提供一个(希望)有趣的列表示例,让我们谈谈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 的所有可能方式。

【讨论】:

    【解决方案2】:
    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]
    

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

    【讨论】:

      【解决方案3】:

      一方面,concatMap 就是 (=&lt;&lt;)。而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"]
      

      或许更实际一点,你可以使用 applicative 实例将两个列表组合在一起

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

      【讨论】:

      • 这似乎与通常理解的“拉链”没有太大关系。
      • zipWith' 并不是它声称的那样。也许您正在寻找ZipList 应用程序?
      猜你喜欢
      • 1970-01-01
      • 2012-03-14
      • 1970-01-01
      • 1970-01-01
      • 2021-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-01
      相关资源
      最近更新 更多