表示法和绑定签名

时间:2018-12-14 19:30:12

标签: haskell monads do-notation

我是Haskell和函数式编程的新手,我想知道为什么这样的示例(“嵌套循环”)有效:

re.sub(r'\u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]', '\n', s) 

下面的某些内容是伪Haskell语法,但我希望它能说明我的理解。

据我了解,它已经变成了这样的东西

do
  a <- [1, 2, 3]
  b <- [4, 5, 6]
  return $ a * 10 + b

我认为这个表达方式

[1, 2, 3] >>= \a -> 
          ([4, 5, 6] >>= \b -> 
                     return $ b * 10 + a)

产生部分应用功能的列表

[4, 5, 6] >>= \b -> return $ b * 10 + a

连接到

[[40 + a], [50 + a], [60 + a]]

对于最后一步,看起来像这样

[40 + a, 50 + a, 60 + a]

成为

[1, 2, 3] >>= \a -> [40 + a, 50 + a, 60 + a]

我的困境是因为[41, 51, 61, 42, 52, ... ] 的类型似乎与return $ b * 10 + a的类型不同。

绑定签名不应该是这样吗?

[40 + a, 50 + a, 60 + a]

在此示例中,看起来像

 (>>=)  :: m a -> (a -> m b) -> m b

[int] -> (int -> [int -> int -> int]) -> [int -> int]

3 个答案:

答案 0 :(得分:3)

我认为它令人困惑的原因是因为您正在尝试由内而外地工作,通过尝试将内部绑定视为产生部分应用函数的列表。它不会:ab被关闭,而不是等待应用的参数。相反,请从表达式的外部开始并向内工作:

[1, 2, 3] >>= \a -> (...)

对于列表中的每个项目,以某种方式生成一个列表,并可以访问a作为原始列表中某个项目的名称

... [4, 5, 6] >>= \b -> (...)

要生成上一步所需的列表,请生成一个新列表,可以同时访问ab,这是两个编号列表中的一个。

... return $ b * 10 + a

要生成上一步所需的列表,请创建一个单个项目的列表,其值为b * 10 + a

您问为什么return $ b * 10 + a的类型不同于[40 + a, 50 + a, 60 + a]的类型,但是却没有:两者都是[Int]的类型。都不涉及任何功能。相反,它们都是数字列表,通过引用已经关闭的变量来构造。实际上(>>=)确实具有应有的类型:它接受一个int列表,以及一个用于从单个int生成int列表的函数,并返回不同的int列表:

(>>=) :: [Int] -> (Int -> [Int]) -> [Int]

答案 1 :(得分:2)

这是它的减重方式和运作方式。您说对了:

do
  a <- [1, 2, 3]
  b <- [4, 5, 6]
  return $ a * 10 + b

对这个有好处:

[1, 2, 3] >>= \a -> 
  [4, 5, 6] >>= \b -> 
    return $ b * 10 + a

依次使用Monad的列表实例,我们可以内联它们的>>=return(或pure)的定义:

concatMap
  (\a -> concatMap
    (\b -> [b * 10 + a])
    [4, 5, 6])
  [1, 2, 3]

我们可以将concatMap分解为concatmap

concat
  (map
    (\a -> concat
      (map
        (\b -> [b * 10 + a])
        [4, 5, 6]))
    [1, 2, 3])

现在我们可以减少这种情况了,在这里我认为您遇到的困难是:减少发生在外部,在这种情况下不会产生部分应用的功能;相反,它在内部lambda a的闭合中捕获 (\b -> …)。首先,我们将(\a -> …)映射到[1, 2, 3]上:

concat
  [ (\a -> concat
      (map
        (\b -> [b * 10 + a])
        [4, 5, 6])) 1
  , (\a -> concat
      (map
        (\b -> [b * 10 + a])
        [4, 5, 6])) 2
  , (\a -> concat
      (map
        (\b -> [b * 10 + a])
        [4, 5, 6])) 3
  ]

==

concat
  [ let a = 1
    in concat
      (map
        (\b -> [b * 10 + a])
        [4, 5, 6])
  , let a = 2
    in concat
      (map
        (\b -> [b * 10 + a])
        [4, 5, 6])
  , let a = 3
    in concat
      (map
        (\b -> [b * 10 + a])
        [4, 5, 6])
  ]

然后我们可以减少内部map

concat
  [ let a = 1
    in concat
      [ (\b -> [b * 10 + a]) 4
      , (\b -> [b * 10 + a]) 5
      , (\b -> [b * 10 + a]) 6
      ]
  , let a = 2
    in concat
      [ (\b -> [b * 10 + a]) 4
      , (\b -> [b * 10 + a]) 5
      , (\b -> [b * 10 + a]) 6
      ]
  , let a = 3
    in concat
      [ (\b -> [b * 10 + a]) 4
      , (\b -> [b * 10 + a]) 5
      , (\b -> [b * 10 + a]) 6
      ]
  ]

==

concat
  [ let a = 1
    in concat
      [ let b = 4 in [b * 10 + a]
      , let b = 5 in [b * 10 + a]
      , let b = 6 in [b * 10 + a]
      ]
  , let a = 2
    in concat
      [ let b = 4 in [b * 10 + a]
      , let b = 5 in [b * 10 + a]
      , let b = 6 in [b * 10 + a]
      ]
  , let a = 3
    in concat
      [ let b = 4 in [b * 10 + a]
      , let b = 5 in [b * 10 + a]
      , let b = 6 in [b * 10 + a]
      ]
  ]

然后我们可以通过将变量替换为其值来简化操作:

concat
  [ concat
    [ [4 * 10 + 1]
    , [5 * 10 + 1]
    , [6 * 10 + 1]
    ]
  , concat
    [ [4 * 10 + 2]
    , [5 * 10 + 2]
    , [6 * 10 + 2]
    ]
  , concat
    [ [4 * 10 + 3]
    , [5 * 10 + 3]
    , [6 * 10 + 3]
    ]
  ]

并减少对concat的呼叫:

concat
  [ [ 4 * 10 + 1
    , 5 * 10 + 1
    , 6 * 10 + 1
    ]
  , [ 4 * 10 + 2
    , 5 * 10 + 2
    , 6 * 10 + 2
    ]
  , [ 4 * 10 + 3
    , 5 * 10 + 3
    , 6 * 10 + 3
    ]
  ]

==

[ 4 * 10 + 1
, 5 * 10 + 1
, 6 * 10 + 1
, 4 * 10 + 2
, 5 * 10 + 2
, 6 * 10 + 2
, 4 * 10 + 3
, 5 * 10 + 3
, 6 * 10 + 3
]

当然还有各个表达式:

[ 41, 51, 61
, 42, 52, 62
, 43, 53, 63
]

当您使用列表的Applicative实例时,会 看到部分应用的功能的列表,例如,等同于您的代码:

(\a b -> b * 10 + a) <$> [1, 2, 3] <*> [4, 5, 6]

列表的<$> / fmap的定义仅为map,因此我们部分应用了lambda的第一个参数,生成了类型为[Int -> Int]的列表,然后(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b(此处为[Int -> Int] -> [Int] -> [Int]类型)将其左侧操作数中的每个函数应用于其右侧操作数中的每个值。

答案 2 :(得分:1)

请记住,列表monad中的return x = [x]xs >>= f = concatMap f xs。因此

[1, 2, 3] >>= \a -> 
      ([4, 5, 6] >>= \b -> 
                 return $ b * 10 + a)

变成

concatMap (\a -> (concatMap (\b -> [b*10+a]) [4,5,6])) [1,2,3]

(在a函数中,b是一个自由变量)

concatMap (\a -> [4*10+a, 5*10+a, 6*10+a]) [1,2,3]

没有部分应用的函数,只有一个函数使用其参数3次返回一个列表值。然后减少到

[4*10+1, 5*10+1, 6*10+1, 4*10+2, 5*10+2, 6*10+2, 4*10+3, 5*10+3, 6*10+3]

[41,51,61,42,52,62,43,53,63]