5 ms·
I think this looks a lot nicer with Haskell-style do notation: import Control.Monad (guard) import Data.Ratio ((%)) results :: [[Int]] results
by eutectic 10y ago
I think this looks a lot nicer with Haskell-style do notation:
import Control.Monad (guard)
import Data.Ratio ((%))
results :: [[Int]]
results =
do th <- u
o <- u
r <- u
guard $ 2*th^2 + o^2 + r^2 == 1000
s <- u
z <- u
guard $ th^2 * z^2 * (s - z) + s^2 == 7225
g <- u
d <- u
xi <- u
guard $ (g - 1)^2 + d^2 + xi^2 - xi == 600
(et, i) <- [(xi - 7, xi - 11), (xi - 11, xi - 7),
(xi + 7, xi + 11), (xi + 11, xi + 7)]
guard $ 0 < et && et <= 26 && 0 < i && i <= 26
(a, mu) <- [(4, 1), (2, 2), (1, 4)]
pi <- u
guard $ a * (a + pi) == 4 * g
b <- u
n <- u
guard $ b^3 + z^3 + n^3 + o^9 == 1997
k <- [3, 4]
e <- u
guard $ (a % k)^2 + (b % n)^2 + (d % xi)^2 +
(e % pi)^2 + (et % mu)^2 + (i % s)^2 == 6
return [a, b, g, d, e, z, et, th, i, k, th, mu, n, xi, o, pi, r, s]
where u = [1..26]
main :: IO ()
main = print results
- deleted 10y ago[deleted]
- danidiaz 10y agoJust for fun, this part (et, i) <- [(xi - 7, xi - 11), (xi - 11, xi - 7), (xi + 7, xi + 11), (xi + 11, xi + 7)] Could be written as (et,i) <- do (a,b) <- let tuple = (7,11) in [tuple, swap tuple] sign <- [id,negate] [(xi+sign a,xi+sign b)]
- brilee 10y agoIt amazes me that Haskell takes my Python hack and actually allows it to be expressed quite naturally! I'm curious what the semantics are for the interleaving of do and guard - would you say that Haskell will compile that code to the equivalent nested for loops, with the same short-circuiting as in the Python version? Or is there something fancier going on?
- conistonwater 10y agoThere isn't any magic going on, but remember that Haskell's lists are lazy: to write a truly equivalent version in Python would be a little tricky because you'd have to deal with generators. But the conversion from do+guard to list comprehensions is really simple (see the definition in source at https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GHC.Base.html#line-748 https://hackage.haskell.org/package/base-4.9.0.0/docs/src/GH... and use the way the do-notation desugars into applications of (>>=) and (>>)), the definition of guard itself is similarly trivial: https://hackage.haskell.org/package/base-4.9.0.0/docs/src/Control.Monad.html#guard https://hackage.haskell.org/package/base-4.9.0.0/docs/src/Co...). It's so much easier to see all this by just working out a small example for yourself with pen and paper. So these two are equivalent, and the list comprehension version is very close to what nested loops (read it from left to right) would give you, except for the laziness. do x <- [1, 2, 3]; guard (f x); y <- [1, 2, 3]; guard (g x y); z <- [1, 2, 3]; guard (h x y z); return (x, y, z) [(x, y, z) | x <- [1, 2, 3], f x, y <- [1, 2, 3], g x y, z <- [1, 2, 3], h x y z] for x in [1, 2, 3] { if (f(x)) { for y in [1, 2, 3] { if (g(x,y)) { for z in [1, 2, 3] { if (h(x,y,z)) { yield((x, y, z)) }}}}}} -- "pseudocode" The guard uses the following idea: guard True = [()] guard False = [] So _ <- guard True is a noop, while _ <- guard False short-circuits the list comprehension the way "continue" would (just like "for x in [] { ... }"), so guard (f x) becomes the test f x in the list comprehension.
- wz1000 10y ago> It amazes me that Haskell takes my Python hack and actually allows it to be expressed quite naturally! This isn't an accident. Python's list comprehensions were modeled on Haskell's list comprehensions, which were in turn modeled on the much more general concepts of monads. Haskell's do notation provides a nice syntactic sugar for monadic code. Since lists form a monad, such code is quite naturally expressed using do notation. Haskell's list comprehensions can be directly translated to do notation [x+y | x <- [1..10], y <- [1..10], x+y<5] can be translated into do x <- [1..10] y <- [1..10] guard (x+y<5) return (x+y) which is syntactic sugar for [1..10] >>= (\x -> [1..10] >>= (\y -> guard (x+y<5) >> return (x+y))) where (>>=) is the monadic bind operator.
- groovy2shoes 10y agoHave an upvote for the fun-factor of your comment, but know that I find the aesthetics of the two to be about the same.
- pmontra 10y agoThanks for that code. I don't know anything about Haskell so I attempt a UX test: results is an array of Int, but do the double [[ ]] brackets mean something special? I hope they do, otherwise why planting doubts in people's mind. <- is assignment or pattern matching, probably the latter. the $ after guard is... what? Maybe a noop like Python's : Maybe guard $ expression something is equivalent to if expression something If this is the case, it's much less readable because people are left guessing. The lack of indentation doesn't help either. u is probably the variable that's iterated over in where u = [1..26] Not very readable to put that after every use of u. This is easier to read where u = [1..26] do which is many languages is for u in [1..26] do I can imagine that where is more general and is used in other contexts. Don't take these as criticisms of Haskell, only as a UX test. Python's list comprehensions have never been very readable to me. This Haskell loop is somewhat better. I'm sure there are good reason for designing Haskell's syntax like that. Programming languages belong to lineages and tend to perpetrate the syntax of their parents. It makes them easier to adopt to developers well versed in their parent languages but that could also make them difficult to grasp for developers of other language families. Maybe luring imperative developers into the functional world is not one of the goals of Haskell and Haskell has achieved its goals as it is. Still I believe that the first functional language that looks like an imperative language will move many developers from OOP to functional. Elixir attempted that, consciously or not, but it's not enough. The barrier there are the OTP patterns, which are not easy to use properly. There are many different choices for writing a process (what in OOP would be an object with its own CPU) and its hard to chose the proper one. GenServer, Agent, Task? Nobody is asking these kind of questions in Ruby or Python https://groups.google.com/forum/#!topic/elixir-lang-talk/DCTXyGV791w https://groups.google.com/forum/#!topic/elixir-lang-talk/DCT... There is also the code to add them to the supervision tree (the cool systemd-inside-the-language thing of Erlang and Elixir). I can't help thinking that Elixir has to be streamlined.
- randomstring 10y agoI was about to observation that the python code as posted in the blog missed an important optimization, reordering some of the tests to: for b in range(1, 27) for n in range(1, 27) if b**3 + z**3 + n**3 + o**9 == 1997 for k in (3,4) for e in range(1, 27) This reduces the number of innermost tests and on my laptop it runs about 18x faster than the blog's solution. That was: for k in (3,4) for b in range(1, 27) for e in range(1, 27) for n in range(1, 27) if b**3 + z**3 + n**3 + o**9 == 1997 Then I see that @eutectic did the same optimization in his Haskell code. Nice!
- wz1000 10y agoJust for fun, guard $ (a % k)^2 + (b % n)^2 + (d % xi)^2 + (e % pi)^2 + (et % mu)^2 + (i % s)^2 == 6 can be written as guard $ (6 ==) $ sum $ map (^2) $ zipWith (%) [a ,b ,d ,e ,et,i ] [k ,n ,xi,pi,mu,s ]
- conistonwater 10y agoAh! But should it be written like that?
- wz1000 10y agoI don't know. To me, sum $ map (^2) Is pretty clear as a sum of squares is a quite common mathematical idiom. The zipWith part could probably be avoided. guard $ (6 ==) $ sum $ map (^2) [(a%k),...]
- conistonwater 10y agoI think the original is still a lot cleaner, though.