jac*_*ick 6 monads haskell do-notation
我是Haskell和函数式编程的新手,我想知道为什么这样的例子("嵌套循环")有效:
do
a <- [1, 2, 3]
b <- [4, 5, 6]
return $ a * 10 + b
Run Code Online (Sandbox Code Playgroud)
下面的一些东西是一种伪Haskell语法,但我希望它能说明我的理解.
这是我的理解,它变成了这样的东西
[1, 2, 3] >>= \a ->
([4, 5, 6] >>= \b ->
return $ b * 10 + a)
Run Code Online (Sandbox Code Playgroud)
我想这个表达方式
[4, 5, 6] >>= \b -> return $ b * 10 + a
Run Code Online (Sandbox Code Playgroud)
生成部分应用函数的列表
[[40 + a], [50 + a], [60 + a]]
Run Code Online (Sandbox Code Playgroud)
连接到
[40 + a, 50 + a, 60 + a]
Run Code Online (Sandbox Code Playgroud)
最后一步,看起来像这样
[1, 2, 3] >>= \a -> [40 + a, 50 + a, 60 + a]
Run Code Online (Sandbox Code Playgroud)
变
[41, 51, 61, 42, 52, ... ]
Run Code Online (Sandbox Code Playgroud)
我的困境是因为类型return $ b * 10 + a
似乎与类型不同[40 + a, 50 + a, 60 + a]
.
绑定签名不应该像这样吗?
(>>=) :: m a -> (a -> m b) -> m b
Run Code Online (Sandbox Code Playgroud)
在这个例子中似乎是这样的
[int] -> (int -> [int -> int -> int]) -> [int -> int]
Run Code Online (Sandbox Code Playgroud)
和
[int] -> (int -> [int -> int]) -> [int]
Run Code Online (Sandbox Code Playgroud)
Here\xe2\x80\x99是它如何脱糖和运行的。你\xe2\x80\x99是对的:
\n\ndo\n a <- [1, 2, 3]\n b <- [4, 5, 6]\n return $ a * 10 + b\n
Run Code Online (Sandbox Code Playgroud)\n\n对此进行脱糖:
\n\n[1, 2, 3] >>= \\a -> \n [4, 5, 6] >>= \\b -> \n return $ b * 10 + a\n
Run Code Online (Sandbox Code Playgroud)\n\n依次使用 的列表实例,我们可以内联其和(或)Monad
的定义:>>=
return
pure
concatMap\n (\\a -> concatMap\n (\\b -> [b * 10 + a])\n [4, 5, 6])\n [1, 2, 3]\n
Run Code Online (Sandbox Code Playgroud)\n\n我们可以分解concatMap
为concat
和map
:
concat\n (map\n (\\a -> concat\n (map\n (\\b -> [b * 10 + a])\n [4, 5, 6]))\n [1, 2, 3])\n
Run Code Online (Sandbox Code Playgroud)\n\n现在我们可以减少这个,我认为这就是您遇到困难的地方:减少是从外向内发生的,并且在这种情况下不会产生部分应用的函数;相反,它捕获 a
内部 lambda 的闭包(\\b -> \xe2\x80\xa6)
。首先,我们(\\a -> \xe2\x80\xa6)
映射[1, 2, 3]
:
concat\n [ (\\a -> concat\n (map\n (\\b -> [b * 10 + a])\n [4, 5, 6])) 1\n , (\\a -> concat\n (map\n (\\b -> [b * 10 + a])\n [4, 5, 6])) 2\n , (\\a -> concat\n (map\n (\\b -> [b * 10 + a])\n [4, 5, 6])) 3\n ]\n\n==\n\nconcat\n [ let a = 1\n in concat\n (map\n (\\b -> [b * 10 + a])\n [4, 5, 6])\n , let a = 2\n in concat\n (map\n (\\b -> [b * 10 + a])\n [4, 5, 6])\n , let a = 3\n in concat\n (map\n (\\b -> [b * 10 + a])\n [4, 5, 6])\n ]\n
Run Code Online (Sandbox Code Playgroud)\n\n然后我们可以减少内部map
s:
concat\n [ let a = 1\n in concat\n [ (\\b -> [b * 10 + a]) 4\n , (\\b -> [b * 10 + a]) 5\n , (\\b -> [b * 10 + a]) 6\n ]\n , let a = 2\n in concat\n [ (\\b -> [b * 10 + a]) 4\n , (\\b -> [b * 10 + a]) 5\n , (\\b -> [b * 10 + a]) 6\n ]\n , let a = 3\n in concat\n [ (\\b -> [b * 10 + a]) 4\n , (\\b -> [b * 10 + a]) 5\n , (\\b -> [b * 10 + a]) 6\n ]\n ]\n\n==\n\nconcat\n [ let a = 1\n in concat\n [ let b = 4 in [b * 10 + a]\n , let b = 5 in [b * 10 + a]\n , let b = 6 in [b * 10 + a]\n ]\n , let a = 2\n in concat\n [ let b = 4 in [b * 10 + a]\n , let b = 5 in [b * 10 + a]\n , let b = 6 in [b * 10 + a]\n ]\n , let a = 3\n in concat\n [ let b = 4 in [b * 10 + a]\n , let b = 5 in [b * 10 + a]\n , let b = 6 in [b * 10 + a]\n ]\n ]\n
Run Code Online (Sandbox Code Playgroud)\n\n然后我们可以通过用变量的值替换变量来简化:
\n\nconcat\n [ concat\n [ [4 * 10 + 1]\n , [5 * 10 + 1]\n , [6 * 10 + 1]\n ]\n , concat\n [ [4 * 10 + 2]\n , [5 * 10 + 2]\n , [6 * 10 + 2]\n ]\n , concat\n [ [4 * 10 + 3]\n , [5 * 10 + 3]\n , [6 * 10 + 3]\n ]\n ]\n
Run Code Online (Sandbox Code Playgroud)\n\n并减少对以下内容的调用concat
:
concat\n [ [ 4 * 10 + 1\n , 5 * 10 + 1\n , 6 * 10 + 1\n ]\n , [ 4 * 10 + 2\n , 5 * 10 + 2\n , 6 * 10 + 2\n ]\n , [ 4 * 10 + 3\n , 5 * 10 + 3\n , 6 * 10 + 3\n ]\n ]\n\n==\n\n[ 4 * 10 + 1\n, 5 * 10 + 1\n, 6 * 10 + 1\n, 4 * 10 + 2\n, 5 * 10 + 2\n, 6 * 10 + 2\n, 4 * 10 + 3\n, 5 * 10 + 3\n, 6 * 10 + 3\n]\n
Run Code Online (Sandbox Code Playgroud)\n\n当然还有个人的表达方式:
\n\n[ 41, 51, 61\n, 42, 52, 62\n, 43, 53, 63\n]\n
Run Code Online (Sandbox Code Playgroud)\n\n您将看到部分应用函数的列表的情况是在使用Applicative
列表实例时,例如,相当于您的代码:
(\\a b -> b * 10 + a) <$> [1, 2, 3] <*> [4, 5, 6]\n
Run Code Online (Sandbox Code Playgroud)\n\n<$>
列表的/的定义fmap
只是map
,因此我们部分应用 lambda 的第一个参数,生成类型 的列表[Int -> Int]
,然后(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
,此处的类型[Int -> Int] -> [Int] -> [Int]
,将其左操作数中的每个函数应用于其右操作数中的每个值。