简体   繁体   中英

Recursive functions in haskell?

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.

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!

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM