有没有办法在列表推导中使用let
,where
或以其他方式定义子表达式,以便它可以在术语和约束中使用?
从我的实验中,以下工作:
[let x = i*i in x | i<-[1..10], i*i > 20] --good
[i*i | i<-[1..10], let x=i*i in x > 20] --good
但这些不是范围:
[let x = i*i in x | i<-[1..10], x > 20] -- 'x' not in scope error
let x = i*i in [x | i<-[1..10], x > 20] -- 'i' not in scope error
[x | i<-[1..10], x > 20] where x = i*i --parse error on 'where'
所以let
在一个地方或另一个地方工作,但不能同时在一起工作!
我发现使其工作的唯一方法(即避免重复的表达式和可能的评估)是添加一个愚蠢的单例列表,就像我在这里用x<-[cat i [1..k]
作为列表理解的约束一样:
> let cat x l = foldl1 (++) $ map show [x*i | i<-l]
maximum [x| i<-[1..9999], k<-[2..div 10 $ length $ show i], x<-[cat i [1..k]], sort x == "123456789"]
"932718654"
或者,继续上面的小例子,
[x | i<-[0..10], x<-[i*i], x > 20] --works
这看起来有点傻,而且略显缺乏清晰度,因为它看起来效率不高。不过,如果let
或where
在整个理解过程中起作用,那就太好了。可以这样做吗?
答案 0 :(得分:23)
你这样写:
[x | i <- [0..10], let x = i*i, x > 20]
请注意,没有in
。您可以在该术语和x
后面的任何约束中引用let
。 let
的这种形式对应于do
中的形式 - 符号:
do i <- [0..10]
let x = i*i
guard (x > 20)
return x
此处x
的范围是let
到do
- 块的结尾。
答案 1 :(得分:5)
你几乎拥有它;您只需撰写[x | i <- [0..10], let x = i*i, x > 20]
(注意,
而不是in
)。它与do
非常相似 - 符号(实际上,您可以使用do
- 符号,而最近的GHC扩展使您可以对任意monad使用列表推导)。如果您很好奇,可以找到语法in the Haskell 98 report:
aexp -> [ exp | qual_1 , ... , qual_n ] (list comprehension, n >= 1)
qual -> pat <- exp (generator)
| let decls (local declaration)
| exp (guard)
正如您所看到的,其中一个有效的限定词是let decls
,这正是您想要的。