4

I am pretty new to programming and I was asked to convert 3 haskell functions into python as a practice exercise. The 3 functions are connected, since output of one is used as the input of the next one and so on.

I get what the haskell functions do, but I have no idea how to start converting them!

This is the haskell code:

factorial :: Int -> Int
factorial n = product (down n)

product :: [Int] -> Int
product [] = 1
product (a:as) = a * product as

down :: Int -> [Int]
down 0 = []
down n = n : down (n-1)

And this was my try of converting it:

class function:
    def down(self):
        if self.n == 0:
            self.lista = []
        else:
            self.lista = range(self.n, 0, -1)

    def product(self):
        for x in self.lista:
            if x == []:
                self.product = 1
            if x != []:
                for i in self.lista:
                    self.product = i * self.product

    def factorial(self):
        self.n = int(raw_input("n="))

        self.fact = self.product(self.down(self.n))

        print self.fact
        raw_input()

c = function()
c.factorial()

Well, first I believe this isn't a 'direct conversion' of the haskell code. That would be okay though, but second, it doesn't work.

This lack of programming background is killing me...can anyone help me with this problem?

Thanks a lot!

Edit:

The point of this issue is to exactly convert the haskell into python. I did a stripped version of it myself, it was the next step in the exercise ^^

Edward Coelho
  • 235
  • 2
  • 14
  • Converting code from one language you don't fully understand to another language you don't fully understand is a terrible way to learn programming. I would suggest to whoever is giving you exercises that they get you to the point of feeling comfortable reading and writing code in one language first. – Ben Nov 02 '12 at 02:51

2 Answers2

7

First off, ditch the class wrapper - that's not needed.

A straight Python translation would be something like:

# factorial :: Int -> Int
def factorial(n):
    return product(down(n))

# product :: [Int] -> Int
def product(arr):
    if len(arr) == 0: return 1
    a, ar = arr[0], arr[1:]
    return a * product(ar)

# down :: Int -> [Int]
def down(n):
    if n == 0: return []
    return [n] + down(n - 1)

But the recursive style is not very Pythonic here. Next exercise: replace recursion with iteration, list comprehensions, or calls to reduce (if on Python2).

Deestan
  • 16,738
  • 4
  • 32
  • 48
  • 2
    final exercise, read about the `factorial` function in the `math` module in the stdlib :D – mgilson Nov 01 '12 at 18:07
  • Oh, I am amazed at how simply this could be! Had no idea haskell was so similar to python. Thanks a lot! – Edward Coelho Nov 01 '12 at 18:07
  • 1
    The problem with this code is not that it's unpythonic (i.e. not idiomatic Python), but that it gives the beginner a false impression of similarity between Python and Haskell. Python's lists are contiguous arrays, so getting the remaining members is an expensive operation for long lists. In other words, Python's `ar = arr[1:]` is quite different in terms of CPU and memory usage from Haskell's unpacking of first and remaining list elements. – user4815162342 Nov 01 '12 at 18:15
  • Ugh, but now how do I make the variables pass from one function to another? This is my first time working with multiple functions, that's why I used the class wrapper ^^' – Edward Coelho Nov 01 '12 at 18:16
  • 1
    @EdwardCoelho Same way as they do in the Haskell source, they are given as parameters in the function calls. :) – Deestan Nov 01 '12 at 18:25
  • @user4815162342 That's what I meant; recursively constructing a list is horribly unpythonic, as it is an approach that doesn't "get" Python. But yes you are entirely correct. :) – Deestan Nov 01 '12 at 18:30
4

If you want to write idiomatic Python, avoid recursion.

down(n) is spelled as range(n, 0, -1). Use xrange if you want lazy semantics, which would be closer to the spirit of Haskell.

product(lst) is reduce(operator.mul, lst, 1). (It would be more idiomatic to just spell out the loop, but this is shorter.)

From there on it should be obvious how to convert factorial.

user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • Wow thanks! I really should be using lazy semantics and didn't know about that! – Edward Coelho Nov 01 '12 at 18:03
  • 2
    If you like lazy semantics, you'll love [generators](http://www.python.org/dev/peps/pep-0255/). Python is lazier than people tend to think. :) – user4815162342 Nov 01 '12 at 18:07
  • @EdwardCoelho You're welcome. If you found the answer useful, feel free to click the "accept" button. :) – user4815162342 Nov 01 '12 at 18:12
  • I certainly did! I'm just trying to finishing the code. I always do that before accepting :p – Edward Coelho Nov 01 '12 at 18:20
  • Would you mind explaining the {reduce(operator.mul, lst, 1)} line a bit further please? – Edward Coelho Nov 01 '12 at 18:24
  • `operator.mul` is the [functional equvalent](http://docs.python.org/2/library/operator.html) of the multiplication operator. The `reduce` operation is equivalent to a loop of: `r = 1; for elem in lst: r *= elem`. Some functional languages call this operation `fold`. – user4815162342 Nov 01 '12 at 23:17
  • 1
    If you like lazy semantics and to be able to use values more than once, you'll hate generators. – Ben Nov 02 '12 at 02:48