有没有办法在列表语法中使用where
表达式?我想像这样的东西会起作用:
foo = [i where i = 1]
但编译产量:
错误:输入'where'
时解析错误
可以在列表中使用let ... in
:
foo = [let i = 1 in i]
所以我假设where
和let
表现为不同的句法结构?
我知道可以这样做:
foo = [i] where i = 1
...但是对于更复杂的列表,i
和i = 1
之间的距离太远而无法清楚 - 请参阅下面的上下文部分并想象更多的测试用例, testGroup
本身嵌套在列表中。
上下文
我试图编写一个可读的测试用例时遇到了这个问题:
testGroup "toInput"
[
testProperty "toInput '*'" (forAll others conversionFails)
where others = arbitrary `suchThat` (\c -> c `notElem` exclChars)
conversionFails c = toInput c == Nothing
]
等同于let
并不满足于阅读imo(更具体地说,当被其他测试用例包围时,testProperty
不会与他们进行相同的缩进更难以遵循)。
testGroup "toInput"
[
let others = arbitrary `suchThat` (\c -> c `notElem` exclChars)
conversionFails c = toInput c == Nothing
in
testProperty "toInput '*'" (forAll others conversionFails)
]
答案 0 :(得分:4)
重要的是要知道
let ... in ...
是一个表达式,也就是说,只要允许表达式就可以写。相比之下,where
绑定到周围的句法结构,就像函数定义的模式匹配行一样。
简而言之,where
绑定到函数声明的子句,而let
更多地局限于表达式。如果您编写where
,则整个子句都可以看到它,例如:
f a | a > 0 = g b b
| otherwise = b
where b = g a a
所以这里where
对于两名警卫是可见的,let
更局部地作用于范围。如果您撰写let y = f x in y * y
,则y
部分in ...
仅可见。例如,我们可以写:
Prelude> let a = 4 in (let a = 2 in a) + a
6
因此,内部a
为2
,而外部为4
。这可能最终会让人感到困惑。如果我们在where
子句中定义具有相同名称的变量,则会导致名称冲突。
如果您的代码示例是单个函数,您当然可以将其移出列表定义,例如:
foo exclChars =
testGroup "toInput" [testProperty "toInput '*'" (forAll others conversionFails)]
where others = arbitrary `suchThat` (`notElem` exclChars)
conversionFails = isNothing . toInput
答案 1 :(得分:2)
[1 | 1==2]
也是列表理解。 1==2
是布尔表达式。
在您的示例foo = [let i = 1 in i]
中,let i = 1 in i
也是一个表达式。
确实where
和let
不同。 let
形成一个表达式,但where
不表达 - 它是定义(a = b where ...
)的一部分。
区别在于,where
定义的变量可用于定义的 guards ,而let
位于=
符号的右侧
对于您的问题,只需在]
之前移动where
,它就会成为定义的一部分。
答案 2 :(得分:1)
您可能期待使用列表推导。在列表推导中,您可以使用|
,这意味着,,您可以说[ i | i <- [1] ]
。因此,您可以按如下方式使用它们:[ ... | others <- [suchThat arbitrary (\c -> notElem c exclChars)]
在您的示例中。
答案 3 :(得分:1)
我不确定你为什么要这个特定的列表理解,但是这样可行,你可以在GHCi中尝试:
> [ i | let i=1 ]
[1]
您无法在列表理解中使用where
来实现此效果。