【发布时间】:2021-12-27 11:52:27
【问题描述】:
我正在学习 Haskell。
我正在尝试查找列表 as 的元素,这些元素与列表 bs 的元素相加,将元素作为元组返回:
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = [(a, a', b) | a <- as, a' <- as, b <- bs, a + a' == b]
代码有效。但是为了学习Haskell,我正在尝试将其重写为do-notation:
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 return ()
然后类型检查器向我抱怨:
• Couldn't match type ‘()’ with ‘(Int, Int, Int)’
Expected type: [(Int, Int, Int)]
Actual type: [()]
平心而论,我知道会的。但是既然我不能跳过 Haskell 中的else 子句,那我应该在else 子句中的return 语句中放入什么?
谢谢。
【问题讨论】:
-
在列表 monad 中,
return (a,a',b)表示单例列表[(a,a',b)],它表示“我找到了一个三元组,(a,a',b),将其添加到结果列表中”。同样,return ()表示[()],即“我找到了一个三元组,(),将其添加到结果列表中”,因为这不是三元组,所以会触发类型错误。要表示“这次我没有找到三元组”,请使用空列表[](没有任何return)。 -
啊。我试过
return [],但显然这也是错误的。我想我还在为do-notation 苦苦挣扎。您是如何到达可以识别何时自动返回的地方的? -
你必须“思考类型”——在 Haskell 中,这是最重要的技能之一。您正在使用单子(列表单子
[]),因此您需要记住什么是随机值以及什么是单子动作。return不是一个神奇的原语,而是一个将任何值x转换为内部只有该值的无聊单子动作的函数(单例列表[x],在这个单子中)。如果你有x = [],你真的需要再次将它包装在一个列表中吗? -
比较列表推导
[ a | a <- [1,2,3] ]和[ a | a <- [[1,2,3]] ]。在前者中,a的范围超过1,2,3,而在后者中,a只有一个值,列表[1,2,3]。那是因为我们添加了一个额外的[.....]。我们可能不希望这样,这与您在[]和return [](即[[]])之间发现的问题相同。在列表 monad 中,仅使用return表示“找到一个值”,而不是“此处未找到值”。 -
好的,所以我今天发现了另一个,那就是 RETURN 仅适用于类型。该提示很有用,并帮助我走上了一条我一直在怀疑但今天才设法确认的道路。但是要确认一下:当您查看一个函数并且它在
do-block 中显示return时,您是否也会自动盯着类型签名以了解正在调用哪个return? (这就是我现在开始做的事情。)
标签: list haskell list-comprehension monads do-notation