Why does this Haskell statement not evaluate lazily?
I have the following function defined:
ex 1 x = 1
ex 0 x = 0
ex b x = b ** x
Then, when I execute the following:
1 `ex`开发者_如何学运维 (sum [1..])
it tries to calculate the sum of the infinite sequence, instead of being lazy and returning 1. Why?
EDIT: Upon further investigation, I found that laziness happens if I define the ex
function in a file, but not if I define it in GHCI:
$ ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help
Loading package base ... linking ... done.
Prelude> let ex 1 x = 1
Prelude> let ex b x = b ** x
Prelude> ex 1 (sum [1..])
<interactive>: out of memory (requested 1048576 bytes)
If I pull the ex
definition into a file (in this case, test.hs):
$ ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help
Loading package base ... linking ... done.
Prelude> :load test.hs
[1 of 1] Compiling Main ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> ex 1 (sum [1..])
1.0
The new question, then, is why?
In GHCi, each let
statement introduces a new definition of ex
, instead of multiple pattern cases as you expect. So it hangs because, when you enter ex 1 (sum [1..])
afterwards, only the final ex b x = b ** x
version exists.
If you want to define a function with multiple pattern cases in GHCi, you'll need to put it in a single let
statement, like this:
let ex 1 x = 1; ex 0 x = 0; ex b x = b ** x
The same applies for anything else that would normally be written across multiple lines, such as do
notation. For instance, a function like this:
f x = do
y <- get
put (x + y)
return y
Would have to be written like this in GHCi:
let f x = do { y <- get; put (x + y); return y }
Prelude> let ex 1 x = 1
Prelude> let ex b x = b ** x
You're not defining a function with two cases here. You define a function with one case, and then you define it again overriding the previous definition.
To define one function with two patterns use let ex 1 x = 1; ex b x = b ** x
, i.e. separate the cases with a semicolon.
I missed one point of laziness, which renders the answer below false.
Because sum
calculates the total sum of all elements in the sequence. Which is, in your case, endless.
You probably want
map ((curry ex) 1) [1..]
That is
map -- map each item x to y
(
(
curry ex -- curry ex, to transform (x, y) -> z into x -> y -> z
)
1 -- then invoke it with 1, which results in y -> z, x being 1
)
[1..] -- the infinite sequence to be mapped.
精彩评论