Home > Net >  List comprehension not ending in a square bracket, console freezing
List comprehension not ending in a square bracket, console freezing

Time:11-02

Entering a list comprehension into GHCi does not generate a list, the final square brackets are missing, and the console freezes. This is what I have come up with:

[13*x   3 | x <- [1..], rem (13*x   3) 12 == 5, mod (13*x   3) 11 == 0, 13*x   3 <= 1000]

I believe the problem lies either with x <- [1..], or 13*x 3 <= 1000. By 13*x 3 <= 1000 I meant to determine the upper limit of the values x in x <- [1..] can take.

I'm given back a result [341, but it does the second square bracket is missing, and the console freezes.

CodePudding user response:

Your program enters an infinite loop.

The first number is 341, but in order to produce the next number, your program keeps looking through all the subsequent values of x, evaluates all the guards for those values, and checks if all the guards are true. The very last guard, 13*x 3 <= 1000 never becomes true again, so the program just keeps enumerating values of x forever. It's looking for the next such x for which all guards are true, and as soon as it finds one, it's going to print it. But such x never comes.

If you want the list to end once x*13 3 > 1000, you have to use takeWhile:

... | x <- takeWhile (\y -> y*13   3 <= 1000) [1..],  ...

That way the list will actually stop when it reaches 1000. No more values of x would be produced.

CodePudding user response:

You're giving the compiler way too much credit. It isn't going to carefully analyse your list comprehension in order to deduce that past a certain point there will be no more results, and it should call the list complete. It only does what you tell it to do.

In this case what you told it to do is:

[ 13*x   3                -- produce numbers of the form 13*x   3
| x <- [1..]              -- by searching all x from [1..]
, rem (13*x   3) 12 == 5  -- allowing only x that meet this condition
, mod (13*x   3) 11 == 0  -- and this condition
, 13*x   3 <= 1000        -- and this condition
]

So it prints [341 and "freezes" because it's still trying to compute the rest of that list. You don't see anything happening, but internally it's drawing ever bigger x from [1..] and diligently checking those conditions to realise that the number shouldn't be included. But it never hits the end of [1..] in order to stop, so it never gets up to printing the ] and waiting for more input.

With your code you are explicitly telling the compiler that you want to search every number in the infinite1 list [1..]. You are then expecting it to notice that 13*x 3 <= 1000 can only be true for x drawn from a finite prefix of [1..] and thus actually not search the entire list [1..] as you instructed2.

That is a perfectly reasonable thing to want, and I can imagine a system capable of pulling that off (at least with simple conditions like this). So testing it out like this to see if it works is a good idea! However unless someone actually told you that figuring out enumeration upper bounds from conditions in list comprehensions is a feature that GHC can provide, it shouldn't be surprising that it never completes when you tell it to search an infinite list.

For this style of list comprehension (getting all numbers in a range meeting certain conditions) you normally shouldn't use [1..] and then try to impose a stopping condition. Just figure out that the last number that will pass 13*x 3 <= 1000 and use [1..76] as your generator instead. You can even have Haskell figure it out for you with [1 .. (1000 - 3) div 13].

You use a generator like [1..] when you want to get all numbers of the right form. Then you can use functions like take or takeWhile to get a finite section at the point where you want to use it for something. e.g.

Prelude> let xs = [13*x   3 | x <- [1..], rem (13*x   3) 12 == 5, mod (13*x   3) 11 == 0]
Prelude> takeWhile (<= 1000) xs
[341]
Prelude> take 5 xs
[341,2057,3773,5489,7205]

In fact the simplest and most direct way to express what you want in a single expression is this:

takeWhile (<= 1000) [13*x   3 | x <- [1..], rem (13*x   3) 12 == 5, mod (13*x   3) 11 == 0]

Everything in a list comprehension (except the generator expression) is only talking about a single element at a time. There's just no way to express concepts that are talking about the returned list as a whole, like "stop searching once the returned numbers go out of this range". But that concept is trivial to express outside of list comprehension as a normal function (takeWhile (<= 1000)). Don't feel like you have to shoehorn your entire computation into a single list comprehension.


1 Strictly speaking it's infinite if you're using a type like Integer (which is the type Haskell will pick without any other code using the result to impose other constraints on the type). If you're using Int then it's technically finite, and your list comprehension will eventually end when it "runs out of numbers". [1..] as a list of Int is still impractically vast for an exhaustive search, however.

But if you use a smaller type, like Word16 (needs to be imported from Data.Word) then you can in fact finish your original list comprehension in a practical amount of time. (Though I had to tweak it a little to make sure the 13*x stuff was computed in a larger type so it doesn't overflow)

Prelude> import Data.Word
Prelude Data.Word> [13*x   3 | x <- [1 :: Word16 ..], let x' = fromIntegral x, rem (13*x'   3) 12 == 5, mod (13*x'   3) 11 == 0, 13*x'   3 <= 1000]
[341]

2 While I'm being pedantic in the footnotes, if your original list comprehension is being evaluated as a list of Int it wouldn't even be valid to just stop after x grows high enough that 13*x 3 <= 1000 fails for the first time. Try this:

Prelude Data.Word> let x = 768614336404564650 :: Int
Prelude Data.Word> 13*x   3 <= 1000
True

This happens because Int does in fact have an upper bound, so a large enough Int will overflow back to negative when you multiply it by 13. So when searching [1..] as [Int] the compiler is in fact right to keep looking past x = 77; there are almost certainly more numbers in your original list comprehension if it's [Int], they just take a long time to reach.

Again a good way to demonstrate is to use a smaller finite type, like Word16. If I use your original list comprehension as [Word16] without modifying it to avoid overflow in the conditions, you get this:

Prelude Data.Word> [13*x   3 | x <- [1..], rem (13*x   3) 12 == 5, mod (13*x   3) 11 == 0, 13*x   3 <= 1000] :: [Word16]
[341,605,209,869,473,77,737]

Even if the compiler was smart enough to know the regions of [1..] that could possibly pass 13*x 3 <= 1000 condition, it's never going to be able to read your mind and know whether the overflow-produced numbers are solutions you intended or are the result of a bug in your code. It just does what you tell it to do.

  • Related