0

I am trying to learn Haskell and was working on a book problem on recursive functions.

> If   X_1 = 1 then X_2 = 1 + X_1 = 2, X_3 = 1 + X_1 + X_2
      or when it is 5,  X_5 = 1 + X_4 + X_3 + X_2 + X_1 = 16, and so forth. 

I tried doing this on haskell:

test :: Int -> Int
test 1 = 1
test n = sum[test n .. test (n-1)]

but the output is always 1. I think I have to do a function guard first and then sum it but I dont know how to do it with recursive behavior.

9
  • 2
    How is problem2 implemented, you've shown us only test? Commented Feb 7, 2013 at 9:21
  • What is the definition of problem2? Commented Feb 7, 2013 at 9:22
  • 1
    Sum of list with one element looks weird. Commented Feb 7, 2013 at 9:22
  • 1
    @ChrisTaylor then it's easy to prove that x_i = 2 ^ (i-1), no recursion is needed. Commented Feb 7, 2013 at 10:06
  • 3
    It's worth mentioning that [test 1 .. test n] does not mean [test 1, test 2, test 3, and so on up to test n]! To see what's going on, try it out with a non-recursive function like square x = x * x and see what you get for [square 1 .. square 5]. Then try [ square i | i <- [1..5] ]and/or map square [1..5]. Commented Feb 7, 2013 at 10:48

2 Answers 2

5

A good place to start is with list comprehensions:

[ test i | i <- [1..5] ]

means

[ test 1, test 2, test 3, test 4, test 5 ]

See if you can solve it now.

Don't forget to add 1!

Sign up to request clarification or add additional context in comments.

1 Comment

I have been getting the same error when I start off a list: Occurs check: cannot construct the infinite type t0 = [t0] in the return type of a call of "test". In the expression: test x. In the expression: [test x | x <- [1..5]]
3

This part of your code is a Haskell range

[test n .. test (n-1)]

Ranges work by figuring out the left number and the right number, and then constructing a list that contains all steps from the left number to the right number. So:

[1 .. 6] --> [1,2,3,4,5,6]
[5 .. 9] --> [5,6,7,8,9]

As you can see, the default step is 1, so if you have a left number that is higher than the right, you will get an empty list:

[4 .. 3] --> []

As an aside, You can override the default step by providing another number:

[1, 3 .. 6] --> [1,3,5] -- step is 2
[8, 6 .. 3] --> [8,6,4] -- step is -2

As you can see, when you have another step size than 1, you have to be careful with what gets included in the resulting list. This goes especially for negative steps, and even more if you have non-integer steps like [1, 1.25, .. 2.1]. You should almost never generate a list of non-integer numbers using a range.

In your solution you have the line

test n = sum[test n .. test (n-1)]

According to the rules for ranges, this is bound to go wrong. When the program tries to make the list from the range, it tries to compute test n since that is the left number of the range. But that gets us nowhere, since test n is what this whole line is trying to compute in the first place. So we have an infinite loop, and the program hangs.

You could try to do

test n = sum[1 .. test (n-1)]

That looks closer to the examples you gave. It starts with 1 (which is test 1), and ends with test (n-1). But the problem is those values in between. Because ranges have the step of one, what you end up with is:

[1 .. test (n-1)] --> [1,2,3, ......., test (n-1)]

which is not the same as

[test 1, test 2, test 3, .... , test (n-1)]

And since a range can only have a constant step, there is no way to get this last line with a simple range, even if you override the default step. One hint on how to solve this is to notice the number of elements in the list.

length [1 .. test (n-1)] --> test (n-1), 
  -- because [1,2,3] has 3 elements, [1,2,3,4] has 4 and so on

length [test 1, test 2, test 3, ....... , test (n-1)] --> n-1
  -- this is not quite Haskell syntax

The Haskell way here is to make a list that has the correct number of elements, and then transform it so each element is the correct one. How do you make a list of (n-1) elements? Simple:

[1..(n-1)]

From here you can go several ways. There is the list comprehension from luqui:

[test x | x <- [1..(n-1)]]

You can think of this as taking each number out of the range, assigning it to x and then applying the test function to x, so you get [test 1, test 2, test 3, ....... , test (n-1)]. Another way would be to use the map function:

map test [1..(n-1)]

I think of this as applying test to each element of the list at the same time, but it is exactly the same thing as the list comprehension, just two ways of looking at it. Notice that both ways use the [1..(n-1)] range.

If you use either of these instead of the [test n .. test (n-1)] range in your original code, you are very close to the solution. The only thing missing, as luqui reminds, is to remember to add the 1.

2 Comments

Thank you for your detailed explanation. I understand the approach of first creating and then mapping the range with 'test' However, my main concern always have been compiler errors when creating lists/mapping. This is the code I have with this error: test 1 = 1 test n = map test [1..(n-1)] Error: Occurs check: cannot construct the infinite type: b0 = [b0]. Expected type: a0 -> b0, Actual type: a0 -> [b0]. In the first argument of 'map', namely 'test'. in the expression: map test [1..(n-1)] ^I understand I am going from an integer to a list of integers but how do I fix that?
@Razshan: Yes, the error comes because in the first line, the right hand side is a single number, and in the second line, it is a list of something. It is always a good idea to make type declarations (such as test :: Int -> Int) for your functions. Then you will get a slightly better error message. Ok, so you know that you need a single integer as the result in the last line. What do you need to do with the list that you have? You need the sum of all the elements, right? Well, you already have the solution for that in your original code. Then remember to add 1 to this sum :) Hope it helped.

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.