简体   繁体   中英

Haskell - a very simple function

I'm trying to understand the following function:

q1 :: [Int]  -> Int
q1 []                    = 0
q1 [x]                   = x
q1 (x:_:xs)              = max x (q1 xs)

When inputting this: q1 (map abs [-1,-6,-5,7]), it gets me 5. Can someone walk me through why this happens? I understand how map functions, but the pattern matching (x:_xs) is a bit confusing. Thanks!

A list in Haskell is - at least conceptually - a linked list . there are two possibilities:

  • an empty list [] ; or
  • a "cons" (x:xs) where x is the head (first item), and xs the tail (the rest of the list).

Haskell also uses syntactical sugar . For instance [1] is behind the curtains translated to (1:[]) , and [1,4,2,5] to (1:(4:(2:(5:[])))) .

Why is this important? We first will try to understand the q1 function. If we look at the type, we see that q1 takes as input a list of Int s, and returns an Int . It is defined recursively as:

q1 :: [Int]  -> Int
q1 [] = 0
q1 [x] = x
q1 (x:_:xs) = max x (q1 xs)

This means that q1 for an empty list is zero ( 0 ); that the q1 for a list with one element x is x . For a list with two or more elements is the maximum of the first item of that list x , and tail of the tail of that list. This is because we pattern match with (x:_:xs) which is short for (x:(_:xs)) . The underscore basically means " don't care ". So the list should be a cons where the tail is a cons as well, and we are interested in the head of the list x , and the tail of the tail of the list xs .

If we reason about this, we thus find out that q1 returns the maximum of the elements at odd indices (so the first, third, fifth, etc. element). In case the list has an even length, we also calculate the maximum with zero (so in case the all elements at odd indices are negative, the function will return zero, but this only in the case we have a list of even length).

Now if wel call it with q1 (map abs [-1,-6,-5,7]) , it thus means that we will call q1 on the result of map abs on [-1, -6, -5, 7] . map abs constructs a list where abs is applied to all elements of the list (although it is applied lazily). So after the map abs [-1, -6, -5, 7] , we obtain the list [1, 6, 5, 7] . Now the elements at the odd indices are 1 and 5 . So q1 will calculate the maximum of these elements and zero (since the length of the list is four, which is even). And max(0, 1, 5) is 5 .

Personally, especially the fact that we also consider zero but only in case the list has an even length, is very " unstable ". It can result in bugs that are hard to understand, since it can be the result of a detail of the function. We can for instance calculate the maximum with zero, regardless of the length of the list:

q2 :: (Num a, Ord a) => [a] -> a
q2 [] = 0
q2 [x] = max 0 x
q2 (x:_:xs) = max x (q2 xs)

Or we can decide to not use zero at all, and not define maximum over an empty list, like for instance:

q3 :: Ord a => [a] -> a
q3 [x] = x
q3 [x,_] = x
q3 (x:_:xs) = max x (q3 xs)

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