haskell - let/where equivalent within list compreh

2019-04-04 07:46发布

问题:

Is there a way to use let,where or otherwise define sub-expressions in a list comprehension so that it can be used both in the term and constraint?

From my experimenting, the following work:

[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

But these do not bc of scope:

[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'

So let works in one place or the other, but not both together!

The only way I've found to make it work (that is, avoid repeated expressions and possibly evaluations) is to add a silly singleton-list as I did here with x<-[cat i [1..k] as a constraint to the list comprehension:

> 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"

Or, contunuing the trivial example above,

[x | i<-[0..10], x<-[i*i], x > 20] --works

This seems a little silly, and is slightly lacking in clarity, tho it doesn't seem too inefficient. Still, it would be nice if let or where worked across the whole comprehension. Can this be done?

回答1:

You write it like this:

[x | i <- [0..10], let x = i*i, x > 20]

Notice there is no in. You can refer to x in both the term, and any constraints following the let. This form of let corresponds to the one in do-notation:

do i <- [0..10]
   let x = i*i
   guard (x > 20)
   return x

Here x is in scope from the let to the end of the do-block.



回答2:

You almost had it; you can simply write [x | i <- [0..10], let x = i*i, x > 20] (note the , instead of in). It's very similar to do-notation (in fact, you can use do-notation instead, and a recent GHC extension enables you to use list comprehensions for arbitrary monads). If you're curious, you can find the syntax 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)

As you can see, one of the valid qualifiers is let decls, which is exactly what you wanted.