-2

I just started learning Haskell and I'm trying to define the "Get n'th element of list" (with the !! operator) in a function which uses foldl. I now defined it without foldl, just making use of recursion. I wondered if anybody could tell me how to change the code I have to a function with foldl, and could describe what is happening. Thanks in advance!

get 1 (x:_) = x
get i (_:xs) = elementAt'' (i - 1) xs
Steven
  • 1,123
  • 5
  • 14
  • 31

2 Answers2

4

A couple of notes:

First note that you want get 1 to return the first element in your list, that's not the common choice in many languages including Haskell ([2, 3, 5] !! 1 = 3).

Second, although elementAt is a recursive function over lists, it can be defined more efficiently in the old fashion recursive way. fold functions are not good choices here, because fold goes through every element of the list. But we want elementAt recursion to stop the moment that we find the element.

Given this, here is how you can implement elementAt recursively:

elementAt :: Int -> [a] -> a
elementAt i (x:xs) = if i == 1 then x else elementAt (i-1) xs

And here's the implementation using foldl:

elementAt' :: Int -> [a] -> a
elementAt' i (x:xs) = 
    snd $ 
        foldl 
            (\(j, a) b -> 
                if j < 1 then (j-1, a) else (j-1, b))
            (i-1, x) 
            xs

The seed value of foldl is a tuple: (i-1, x) where x is the head of the list.

Note that the return result of fold functions must be of the same type of their seed. Hence here foldl returns a tuple: (j-1, a) where a is the final result, if the index is found; otherwise (j-1, b) where b is the current element of the list.

You can see how foldl goes through every element of the list even after it found the element at index that we were looking for (it keeps returning the previous result a that will be the final result).

PS. These elementAt functions are not handling the case for empty lists or when i is greater than the length of the list; hence they're not exhaustive.

homam
  • 1,945
  • 1
  • 19
  • 26
2

I can see the following, a bit cryptic way of using foldl for your purpose (it is using zero-based indexing, but can be changed easily to 1-based):

get i lst= 
    snd $
        foldl (\p (j, y) -> if j == i then (j,y) else p ) (0, 0) (zip [0,1..] lst) 

The foldl part is working with tuples (index, element), whose list is generated by zipping the given list with indices list. The function passed to foldl as first argument is comparing the index of the desired element with the index with currently passed, and returning the current element if the index is matching, or the previous element otherwise. Then, in the end by using snd only the element part of the tuple is returned.

Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61