List Monad 与 fmap 的使用
Use of List Monad vs fmap
list monad 是否有任何不只是滚动到 fmap 的实际用途?什么时候将 bind over fmap 与列表 monad 结合使用?
例如,您可以执行 [1,2,3] >>= return . ( + 1)
,但这与 (+1) <$> [1,2,3]
相同 - 您什么时候在列表中使用没有 return 的绑定?
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]
对于不像 *
这样简单的操作,因此您无法避免像这样的蛮力方法,这实际上可能是一个很好的解决方案。
将 bind 与 return 结合使用等同于使用 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 的所有可能方式。
一方面,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]
list monad 是否有任何不只是滚动到 fmap 的实际用途?什么时候将 bind over fmap 与列表 monad 结合使用?
例如,您可以执行 [1,2,3] >>= return . ( + 1)
,但这与 (+1) <$> [1,2,3]
相同 - 您什么时候在列表中使用没有 return 的绑定?
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]
对于不像 *
这样简单的操作,因此您无法避免像这样的蛮力方法,这实际上可能是一个很好的解决方案。
将 bind 与 return 结合使用等同于使用 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 的所有可能方式。
一方面,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]