Roland
Roland

Reputation: 9721

ruby while loop translated to haskell

I've just started learning a bit of Haskell and functional programming, but I find it very difficult getting a hang of it :)

I am trying to translate a small piece of ruby code to Haskell (because I like the concept functional programming and Haskell proposes and even more because I come from a mathematics field and Haskell seems very mathematical):

class Integer
  def factorial
    f = 1; for i in 1..self; f *= i; end; f
  end
end

boundary = 1000
m = 0

# Brown Numbers - pair of integers (m,n) where n factorial is equal with square root of m

while m <= boundary

    n = 0

    while n <= boundary
        puts "(#{m},#{n})" if ((n.factorial + 1) == (m ** 2)) 
        n += 1
    end

    m += 1
end

I could only figure out how to do factorials:

let factorial n = product [1..n]

I cannot figure out how to do the while loops or equivalent in Haskell, even though I found some examples that were far to confusing for me.

The idea is that the loops start from 0 (or 1) and continue (with an increment of 1) until it reaches a boundary (in my code is 1000). The reason there is a boundary is because I was thinking of starting parallel tasks that do the same operation but on different intervals so the results that I expect are returned faster (one operation would be done on 1 to 10000, another on 10000 to 100000, etc.).

I would really appreciate it if anyone could help out with this :)

Upvotes: 3

Views: 280

Answers (2)

Luis Casillas
Luis Casillas

Reputation: 30237

To add to shree.pat18's answer, maybe an exercise you could try is to translate the Haskell solution back into Ruby. It should be possible, because Ruby has ranges, Enumerator::Lazy and Enumerable#flat_map. The following rewritten Haskell solution should perhaps help:

import Data.List (concatMap)

results :: [(Integer, Integer)]
results = concatMap (\x -> concatMap (\y -> test x y) [1..1000]) [1..1000]
    where test x y = if fac x == y*y then [(x,y)] else []
          fac n = product [1..n]

Note that Haskell concatMap is more or less the same as Ruby Enumerable#flat_map.

Upvotes: 0

shree.pat18
shree.pat18

Reputation: 21757

Try this:

let results =  [(x,y) | x <- [1..1000], y <- [1..1000] ,1 + fac x == y*y]
               where fac n = product [1..n]

This is a list comprehension. More on that here.

To map it to your Ruby code,

  1. The nested loops in m and n are replaced with x and y. Basically there is iteration over the values of x and y in the specified ranges (1 to 1000 inclusive in this case).
  2. The check at the end is your filter condition for getting Brown numbers.
  3. where allows us to create a helper function to calculate the factorial.

Note that instead of a separate function, we could have computed the factorial in place, like so:

(1 + product[1..x]) == y * y

Ultimately, the (x,y) on the left side means that it returns a list of tuples (x,y) which are your Brown numbers.

OK, this should work in your .hs file:

results :: [(Integer, Integer)] --Use instead of `Int` to fix overflow issue
results =  [(x,y) | x <- [1..1000], y <- [1..1000] , fac x == y*y]
        where fac n = product [1..n]

Upvotes: 8

Related Questions