[Haskell-cafe] Need help of explanation possible!!! Pls

Albert Y. C. Lai trebla at vex.net
Sat Feb 24 14:10:39 EST 2007

```iliali16 wrote:
> take :: Integer -> [Integer] -> [Integer] take _ [] = [] take n xs
> |x < lenghtList xs = (take (n-1) xs :[]) |otherwise = xs I have
> decleared lenghtList before.

The wrong expression "xs:[]" indicates a major misunderstanding; it
worries me alot because you also say you understand well. This makes
help nearly impossible because if I go back to the basic, which is
exactly what is needed, you think you can skip it.

When you're given a list, there are two cases, as you know. One fits the
pattern [], and your code has successfully caught that. The other fits
the pattern x:s, the x being the first element of the list and the s
being the remaining list. I'll say more because I have a point to make.
For example if the given list is [3,1,4], and you match it against the
pattern x:xs, then x refers to 3 and xs refers to [1,4]. The point I
want to make is that x refers to 3, not [3], that x refers to the
number, not a sublist; s does refer to a sublist.

Turn it around, if you have a number 3 and a list of numbers [1,4], you
can put them together, 3 first, with 3:[1,4]. That will give you
[3,1,4]. Again, the point is that it is not [3]:[1,4], that's wrong; it
is 3:[1,4]. Therefore things like [2,1]:[7,5] and [1,4]:[] are also
wrong. (What if you really want to put together [2,1] and [7,5]? There
is a way, but it's off-topic today.)

Now I can talk about take. If the list is too short, you already handle
that, so I just assume the list is long enough, e.g., "take 2
[3,1,4,7]". There is a recursion here, but it's largely on n, not so
much on the list. The base case is "take 0 blahblah", and no matter what
blahblah happens to be, and no matter how long blahblah is, no one
cares, the correct answer is [].

take 0 _ = []

The inductive case is when n is non-zero, and since I can assume the
list is long enough (you already have code for the short case), I can
just assume it matches the pattern x:s (in fact you also already have
code for the [] case). It is possible to apply the pattern x:s to
"decompose" the list and still have the name xs refer to the whole list;
it looks like

take n xs@(x:s) | n < lengthList xs = ...

If the list is [3,1,4,7], then xs is still [3,1,4,7], x is 3, s is
[1,4,7]. What can you do with these data?

You want to extract the first n guys. You can extract the first guy, that is

x

then use recursion to extract n-1 more guys, but skipping x, that is n-1
guys from s but not xs:

take (n-1) s

then you put them together, x first, that is

x : take (n-1) s

The whole line of code looks like

take n xs@(x:s) | n < lengthList xs = x : take (n-1) s

If you call "take 2 [3,1,4,7]", n is 2, x is 3, s is [1,4,7], you get

take 2 [3,1,4,7]
-> 3 : take (2-1) [1,4,7]     we trust in recursion
-> 3 : [1]
=  [3,1]

Eventually someone will tell you that the test "n < lengthList xs" is
unnecessary and, moreover, slows things down tremendously (not just
slightly). Eventually someone will also tell you "what if n is
negative", and you have to make a decision about that. They are right.
But I will call it a day.
```