Do 表示法和列表 monad

dai*_*ish 9 monads haskell list-comprehension list do-notation

我正在学习哈斯克尔。

\n

我正在尝试查找列表中as与列表元素相加的元素bs,并将元素作为元组返回:

\n
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]\nfindSum2 as bs = [(a, a', b) | a <- as, a' <- as, b <- bs, a + a' == b]\n
Run Code Online (Sandbox Code Playgroud)\n

该代码有效。但为了学习 Haskell,我尝试将其重写为do-notation:

\n
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]\nfindSum2 as bs = do\n  a  <- as\n  a' <- as \n  b  <- bs\n  if a + a' == b then return (a, a', b) \n                 else return ()\n
Run Code Online (Sandbox Code Playgroud)\n

然后类型检查员向我抱怨:

\n
   \xe2\x80\xa2 Couldn't match type \xe2\x80\x98()\xe2\x80\x99 with \xe2\x80\x98(Int, Int, Int)\xe2\x80\x99\n      Expected type: [(Int, Int, Int)]\n        Actual type: [()]\n
Run Code Online (Sandbox Code Playgroud)\n

平心而论,我知道会的。但是由于我无法跳过Haskell 中的子句,那么我应该在子句中的语句else中放入什么?returnelse

\n

谢谢。

\n

4ca*_*tle 9

您必须在子句中返回正确类型的内容else。它可以是空列表[],也可以是抽象值之一,例如mzero或empty。

或者您可以删除if表达式并使用该guard函数。

import Control.Monad (guard)

findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = do
  a  <- as
  a' <- as 
  b  <- bs
  guard (a + a' == b)
  return (a, a', b)
Run Code Online (Sandbox Code Playgroud)

通过此实现,您现在还可以将函数签名概括为:

findSum2 :: MonadPlus m => m Int -> m Int -> m (Int, Int, Int)
Run Code Online (Sandbox Code Playgroud)


Wil*_*sem 8

您不能返回单位 ( ()),因为这意味着 和return (a, a', b)具有return ()不同的类型:第一个是[(Int, Int, Int)],而后者是[()]。

你可以做的是使用一个空列表,以防防护失败,所以:

findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = do
  a  <- as
  a' <- as 
  b  <- bs
  if a + a' == b then return (a, a', b) else []
Run Code Online (Sandbox Code Playgroud)