3

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 :)

3
  • 2
    On the very basic level [1..n] generates a list of indices that a normal loop would give you. If each iteration produces on element, use a map. If you need to combine the iterations into one element, use a fold Commented Jul 21, 2014 at 10:44
  • @bartekbanachewicz ~ I confused [1..n] with a range of numbers in ruby, or is it the same thing but it can also be used instead of loop? Commented Jul 21, 2014 at 11:32
  • 2
    It's a range of numbers alright, but there are tools to apply some operation on every element of a range. I just pointed out that [1..n] are values of index in a typical for loop. Commented Jul 21, 2014 at 11:34

2 Answers 2

8

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]
Sign up to request clarification or add additional context in comments.

11 Comments

fac n = product does not compile. Change it to fac n = product[1..n]
My bad. Missed it earlier.
Holy moly, this is so much less code than any other languages I tried :)
@rolandjitsu You are getting integer overflow. Replace Int by the unbounded Integer throughout.
@rolandjitsu Int is a type that is limited in size to a machine word, for speed and space efficiency, so it overflows when you get over that limit. Integer is a type that's based (usually) on the GMP bignum library, it can grow to several megabytes in size.
|
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.