Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create a list in a recursive call where the first element needs to be handled differently?

Tags:

haskell

I am learning functional programming using Haskell. I am trying to create a function that takes a function f, and executes the function on some input x, n number of times.

repeat :: (a -> a) -> a -> Int -> [a]
repeat f x n

So, the output list is like this:

[x, f(x), f(f(x)), ..., f^n(x)]

So far I have been able to come up with a function that I believe does this. It performs n times:

fn f a n = f a : fn f (f a) (n-1)

The problem I have now, is that I want the first element of the list to just be x. Right now, it is starting my list off at f(x). I tried playing around, but I'm not sure how to cover this "base" case in Haskell. Could someone lend a hand?

like image 605
user3236779 Avatar asked Mar 29 '21 23:03

user3236779


People also ask

Is recursion clearly list the condition required for a function to be recursive?

A proper recursive function must always have a base case: The base case is a way to return without making a recursive call. In other words, it is the mechanism that stops this process of ever more recursive calls and an ever growing stack of function calls waiting on the return of other function calls.

Can a recursive function have more than one base case?

A recursive implementation may have more than one base case, or more than one recursive step. For example, the Fibonacci function has two base cases, n=0 and n=1.

How does a recursive function call itself?

A recursive function calls itself, the memory for a called function is allocated on top of memory allocated to calling function and different copy of local variables is created for each function call.

What is the base case of a recursive call?

The base case occurs when n is zero, at which point recursion stops. In the recursive call, the argument is one less than the current value of n, so each recursion moves closer to the base case. Note: For simplicity, countdown () doesn’t check its argument for validity.

Why recursion is used in recursion?

Recursion provides a clean and simple way to write code. Some problems are inherently recursive like tree traversals, Tower of Hanoi, etc. For such problems, it is preferred to write recursive code. We can write such codes also iteratively with the help of a stack data structure.

What is recursive algorithm?

The process in which a function calls itself directly or indirectly is called recursion and the corresponding function is called as recursive function. Using recursive algorithm, certain problems can be solved quite easily. Examples of such problems are Towers of Hanoi (TOH), Inorder/Preorder/Postorder Tree Traversals, DFS of Graph, etc.


2 Answers

You apply the function in two places: once in the "simple" first element case and again in the recursion.

fn f a n = f a : fn f (f a) (n-1)
           ^^^         ^^^

If you want the first element to be a, simply remove the first one; you don't need it.

fn f a n = a : fn f (f a) (n-1)

Now we have a working function, but it will run forever. You also need a base case. If we tell a function to repeat zero times, we should get the empty list.

fn _ _ 0 = []
fn f a n = a : fn f (f a) (n-1)

Finally, for readability and by usual Haskell coding standards, we'll want to give this thing a type signature. The most general possible type, which we can query from ghci is

*Main> :t fn
fn :: (Eq t1, Num t1) => (t2 -> t2) -> t2 -> t1 -> [t2]

But it's highly unlikely we're ever going to call this thing with non-Int values, and the extra typeclasses may just confuse the inference engine when trying to call it later, so I recommend

fn :: (a -> a) -> a -> Int -> [a]
fn _ _ 0 = []
fn f a n = a : fn f (f a) (n-1)
like image 72
Silvio Mayolo Avatar answered Oct 14 '22 10:10

Silvio Mayolo


I would advise to implement this with two functions: one where we construct an infinite list, so (a -> a) -> a -> [a], and then we can construct a function Int -> [a] -> [a] which takes the first n items. Both functions exist in Haskell's base package. Indeed, the fn function is implemented as iterate :: (a -> a) -> a -> [a] and take :: Int -> [a] -> [a].

We can implement iterate as:

iterate :: (a -> a) -> a -> [a]
iterate f = go
    where go x = x : go (f x)

here x : thus yields x and makes a recursive call with go (f x) so the recursive call will yield f(x), and then f(f(x)), etc.

The take function can be implemented as:

take :: Int -> [a] -> [a]
take n _ | n <= 0 = []
take _ [] = []
take n (x:xs) = x : take (n-1) xs

Your fn function is then:

fn :: (a -> a) -> a -> Int -> [a]
fn f x n = take n (iterate f x)

Now we thus have two extra utility functions that can be used, not per se for our fn function.

like image 29
Willem Van Onsem Avatar answered Oct 14 '22 10:10

Willem Van Onsem