【问题标题】:How to properly use guards in Haskell?如何在 Haskell 中正确使用守卫?
【发布时间】:2017-07-26 18:15:42
【问题描述】:

我是 Haskell 的新手,我正在尝试编写简单的函数来让自己习惯语法,我想编写自己的函数来将某个元素添加到特定索引处的列表中。这是我在 Atom(我的文本编辑器)中写的:

addElem :: a->[a]->Int->[a]
addElem elem list index
 | index <= 0            = elem:list
 | index < (length list) = a ++ (elem:b) where a = take index list; b = drop index list
 | otherwise             = list

这个想法是只要索引是Int 并且elemlist 的元素类型相同,它就不会出错,但是当我尝试将其加载到 ghci 中时,我得到了“在 `|' 上解析错误。”我需要限制参数的类型吗?我正在阅读 Learn You A Haskell,但我还没有完全解释缩进如何工作的部分,所以我的错误也可能存在。

【问题讨论】:

  • where 块未附加到表达式,即 x where decls 不是表达式(与 let decls in x 相比,它是表达式)。 where 块必须附加到声明中,在这种情况下,您可能希望它附加到 addElem 声明,因此必须放在声明主体之后,其中最后一个保护语句也构成了一部分。请注意,解析错误永远不会与使用错误的类型有关——这意味着编译器甚至不理解您的代码,更不用说判断它是错误的了。这也不是缩进错误。
  • @user2407038 这应该是一个答案
  • 如果index 太大,你真的要忽略elem 吗?与index otherwise = list ++ [elem] 会有意义。

标签: list function haskell


【解决方案1】:

where 块需要出现在整个函数的末尾,并且在所有情况下共享。您可能打算使用let

addElem :: a -> [a] -> Int -> [a]
addElem elem list index
 | index <= 0            = elem:list
 | index < (length list) = let a = take index list; b = drop index list in a ++ (elem:b)
 | otherwise             = list

另外,请注意let 可以更简洁地写成let (a,b) = splitAt index list in ...splitAt 也在前奏曲中。当然,您也可以将where 块移动到函数的末尾(Haskell 的懒惰使这很容易推理)。

addElem :: a -> [a] -> Int -> [a]
addElem elem list index
 | index <= 0            = elem:list
 | index < (length list) = a ++ (elem:b)
 | otherwise             = list
 where
   a = take index list
   b = drop index list

就我个人而言,我不太喜欢这个,因为它表明ab 可以在函数的其他地方使用。

2010 Haskell Report 的第 4.4.3 节更详细地介绍了允许使用 where 的位置。

【讨论】: