1

I am trying to return the first k elements of a list xs. For example take 2 [1; 2; 3] should output [1; 2].

I wrote a function, but instead of returning the first k elements it returns the last k elements, and instead of returning the whole list if k equals to length of the list, it fails.

This is my code

let rec take k = function
|[] -> failwith "take"
|x :: xs -> if k = List.length xs  then xs else take (List.length xs - k) xs

How can I fix this?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Uysal M
  • 87
  • 6
  • 1
    see https://stackoverflow.com/questions/19944969/prolog-accumulators-are-they-really-a-different-concept/19951540#19951540 for some (hopefully helpful) verbiage. It is language-agnostic, contrary to the title. (also, [this](https://stackoverflow.com/questions/27803152/sicp-example-counting-change-cannot-understand/27804489#27804489) or [this](https://stackoverflow.com/questions/12659581/functional-programming-lots-of-emphasis-on-recursion-why/12662393#12662393)). – Will Ness Oct 21 '16 at 08:24

2 Answers2

3

You need to answer the two basic questions for writing recursively:

  1. What cases are so trivial that the answer is obvious?
  2. If given a non-trivial case, how do I make a smaller instance of it, so I can get the answer for that smaller instance just by using this same function that I am writing1, and then find my full answer from that smaller answer by some simple steps?

For this problem, the trivial cases (I would say) are when k is 0, and when the given list is an empty list.

For the non trivial case, the insight seems to be that I can get the first k elements of a list (for k > 0) by getting the first (k - 1) elements of the tail of the list, and then adding the head of the original list onto that 2.

This is not what your code does, so that's basically why it doesn't work :-)

As a side comment, I wouldn't consider it an error to ask for the first 0 elements of any list (including the empty list).


1 and the essence of recursion method is to assume you already have it at your disposal.

2 this is because of the identity length (x :: xs) = 1 + length xs.

Jeffrey Scofield
  • 65,646
  • 2
  • 72
  • 108
  • So I should replace failwith with if k = 0 then [] , and for the second line if k = 0 then [] else take (k-1) xs @ [x] ? I will give it a look again and try to solve it. Right now I can hardly understand any of this :-). Thank you. – Uysal M Oct 20 '16 at 23:30
  • 1
    You want to put the head back on the beginning. `take (k - 1) xs @ [x]` puts it at the end. Hint: use the `::` operator. As a side comment, learning to think recursively is a real pleasure (IMHO). – Jeffrey Scofield Oct 20 '16 at 23:32
0

Just a different way :

let take k l =
  let v =Array.of_list l in
  let v'=Array.make k 0 in
  Array.blit v 0 v' 0 k;
  Array.to_list v';;

test:

# take 2 [1;2;3];;
- : int list = [1; 2]
# take 0 [1;2;3];;
- : int list = []
# take 4 [1;2;3];;
Exception: Invalid_argument "Array.blit".
V. Michel
  • 1,599
  • 12
  • 14