Home > Enterprise >  How do you write more than 1 line in an if statement in Haskell
How do you write more than 1 line in an if statement in Haskell

Time:10-28

I have an if-else statement, and in the else block I want it to first recurse to the function, except for the last two elements of the list, and then return two elements.

In the following function, after the if-else statement, I have 2 lines of code. however this doesnt compile. I believe the compiler reads these two lines as a single line of code. How do you fix that?

doubleEveryOther :: [Integer] -> [Integer] --outputs the input list, but every 2nd element(from the right) is doubled
doubleEveryOther [] = []
doubleEveryOther x = if (length x <2)
                        then
                            x
                        else
                            doubleEveryOther init (init x) -- These two lines
                            [2*last(init x), last x]       -- These two lines

The compiler says:

    * Couldn't match expected type: [Integer]
                  with actual type: [a0] -> [a0]
    * Probable cause: `init' is applied to too few arguments
      In the first argument of `doubleEveryOther', namely `init'
      In the expression: doubleEveryOther init (init x)
      In the expression:
        [doubleEveryOther init (init x), 2 * last (init x), last x]
   |
19 |                             [doubleEveryOther init (init x), 2*last(init x), last x]
   |

CodePudding user response:

I think you are just missing the append operator :

doubleEveryOther (init (init x))
     [2 * last (init x), last x]

CodePudding user response:

You can not return two lists. If you have two results you want to combine, you use some function, like ( ) :: [a] -> [a] -> [a].

That being said, you here don't need this. You can work with simple pattern matching:

doubleEveryOtherFromLeft :: Num a => [a] -> [a]
doubleEveryOtherFromLeft (x:y:xs) = 2*x : y : doubleEveryOtherFromLeft xs
doubleEveryOtherFromLeft xs = xs

then our doubleEveryOther can reverse the list twice:

doubleEveryOther:: Num a => [a] -> [a]
doubleEveryOther = reverse . doubleEveryOtherFromLeft . reverse

CodePudding user response:

I have an if-else statement, and in the else block I want it to first recurse to the function, except for the last two elements of the list, and then return two elements

OK. I sort of understand what you're doing. The function name is good - the best name is verb-noun, here doubleEveryOther. However, the code looks a lot like Lisp, probably Scheme - the repeated use of init gives it away. That's not how you write Haskell. (I also write Lisp in Haskell syntax too much...)

Haskell recursion works using pattern matching.

lst = [2,3,4]
1 : [2,3,4]   -- [1,2,3,4]

lst = [1,2,3,4]
(x:xs) = lst  -- x is 1, xs = [2,3,4]

So, in this case, you want to match your list against x:y:xs:

lst = [1,2,3,4]
(x:y:xs) = lst  -- x is 1, y is 2, xs=[3,4]

Hence:

doubleEveryOther :: Num a => [a] -> [a]
doubleEveryOther [] = []
doubleEveryOther [x] = [2*x]
doubleEveryOther (x:y:xs) = (2*x):doubleEveryOther xs

Please note the number of special cases which need to be handled. If I am given an empty list, I should return an empty list. If I am given a single value, I need to double it (in analogy to your if .. else clause). If I am given two or more values, this matches x=first, y=second, xs=[] or more.

As for returning more than one value, you can return only one thing from a function. It can be a single value, a single tuple, a single list, and so on.

In this case, you have written a function which says doubleEveryOther - good - but then you want to return the last two values unchanged. You would be better taking off the last two values, running the simple doubleEveryOther and then bolting the last two values on the end. Otherwise, you are overburdening your function.

  • Related