I don't think I understand what's happening here. Shouldn't everything to the right of |
be constraints?
No, at the right part you see an expression that is a comma-separated (,
) list of "parts", and every part is one of the following tree:
- an "generator" of the form
somevar <- somelist
;
- a
let
statement which is an expression that can be used to for instance introduce a variable that stores a subresult; and
- expressions of the type boolean that act like a filter.
So it is not some sort of "constraint programming" where one simply can list some constraints and hope that Haskell figures it out (in fact personally that is the difference between a "programming language" and a "specification language": in a programming language you have "control" how the data flows, in a specification language, that is handled by a system that reads your specifications)
Basically an iterator can be compared to a "foreach" loop in many imperative programming languages. A "let" statement can be seen as introducing a temprary variable (but note that in Haskell you do not assign variable, you declare them, so you can not reassign values). The filter can be seen as an if
statement.
So the list comprehension would be equivalent to something in Python like:
for y in range(100, 1000):
for z in range(y, 1000):
x = y * z
s = str(x)
if x == x[::-1]:
yield x
We thus first iterate over two ranges in a nested way, then we declare x
to be the multiplication of y
and z
, with let s = show x
, we basically convert a number (for example 15129
) to its string counterpart (for example "15129"). Finally we use s == reverse s
to reverse the string and check if it is equal to the original string.
Note that there are more efficient ways to test Palindromes, especially for multiplications of two numbers.
x
is the product of all possiblez
,y
? Now it aims to construct products that are palindromes. – Willem Van Onsem