Achieving a desired number of elements out of a list OCAML

I am trying to take a desired number of elements out of an input list in OCAML. For example, I call my function as “take” which takes two parameters “my_list” and “x”. Here it is:

    let take my_list n =
        let acc = ([],0) in
           let f (current_list, size) x = 
                if n = 0 || n < 0 || size > n then ([],0)
                    else if size < n then  ((x::current_group),size + 1) in
                          let (final_list,_) = List.fold_left f acc n in
                                       List.rev(List.rev final_list)

I got a type error:

This expression has type 'a * 'b but an expression was expected of type 'c list Why is this the case?

I am guessing there is something I need to do with my if…else statement. My reason is that if the desired number of elements I want to take out of my list is true (i.e implement this code if I want to take 3 elements out of the list, no 2), then proceed. Otherwise, keep adding the elements to the current list until it is filled with the desired number of elements I want to take out.

Thank you for any input.

As your TA in the class this was assigned in (-_-), look at the type you return in the if case versus in the else-if and else cases.

5 Likes

Hello,
I still have the same error. I changed my code. Not everyone can get help when they need it, so if you can give constructive input, that would be helpful. Thank you.

If you don’t have an else case on your if statement, OCaml will assume that the type of the entire expression is unit. (This is somewhat more useful when writing code with side-effects.)

In the future, course questions are to be posted in the class discussion forum, as stated in the syllabus.

But I need an else case to handle the scenario that when n is a valid (i.e i want to take 3 elements out of a list of 5 and I cannot take -1 element out of a list etc.).
> let take my_list n =

             let acc = ([],0) in
                   let f (current_list, size) x = 
                           if n = 0 || n < 0 || size > n then acc
                           else ((List.rev(x::current_list)),size + 1) in
                                  let (final_list,_) = List.fold_left f acc my_list in
                                                 List.rev(List.rev final_list)

My output is odd:

take [1;2;3;4;5;6] 3;;

  • : int list = [6]

why is that?
Can we finish up this question on here?
Thanks!

Can you explain why you’re returning what you return in each branch of your if statement?

Also, what each List.rev call does?

(Yes, this is one of those awful Socratic questioning things, btw.)

  1. let take my_list n =
    
  2.     let acc = ([],0) in
    
  3.         if n = 0 || n < 0  then acc
    
  4.             else
    
  5.                    let f (current_list, size) x =  if size < n then ((x::current_list),size + 1) else (current_list, size) in
    
  6.                               let (final_list,_) = List.fold_left f acc my_list in final_list
    

So I modified my code a little bit.
Line 3: if the number of elements I want to take out is 0 or less than 0, then I want to return an empty list.
Line 4: Otherwise, I want to start taking out elements in the list.
Line 5: If the number of elements in my current list is not equal to n, then I want to keep taking elements until I have n of them. Otherwise, I want to return the current list with n elements I extracted.

Thank you, NR!

This code is no longer type-correct; think about what type is returned if n <= 0 versus if n > 0.

Also, there’s a logical error that you’ll notice if you test it out a bit in utop.

if n<=0, the type I want to return is an empty list.
If n > 0, I the type I want to return is also a list.
I tested it out in utop, and the error occurs at

else let f (current_list, size) x = if size < n then ((x::current_list),size + 1) in

Why is there a logical error? My description above does not make sense to you?

Yay, I got it. But it still does not handle the case when n is negative…

take [1;2;3;4;5;6;7;8;9] -1;;
Error: This expression has type int → int list but an expression was expected of type int

OCaml parses take [1;2;3;4;5;6;7;8;9] -1;; as equivalent to (take [1;2;3;4;5;6;7;8;9]) - (1);; – you probably want to change -1 to (-1).

Thank you NR. I appreciate your help.
BTW, can you explain to me why my previous question was an awful socratic question? Thank you.

Oh, I meant that my questions were Socratic questions, and that Socratic questioning can be “awful” when you’re up against a deadline :slight_smile:

NR, I need help.

 let take my_list n =
    let acc = ([],0) in
    if n = 0 || n < 0  then []
       else let f (current_list, size) x = 
           if size < n then (List.rev(x::current_list),size + 1)  else (List.rev current_list,size) in
                 let (final_list,_) = List.fold_left f acc my_list in List.rev(List.rev final_list)

take [1;2;3;4;5;6;7;8;9] 2;;

  • : int list = [1; 2]

take [1;2;3;4;5;6;7;8;9] 3;;

  • : int list = [3; 1; 2] (It should be [1;2;3])

Thank you.

Perhaps move this question to the class forum. But look at what happens for the lengths of 4, 5, etc.

Should I paste my whole code there? People may copy my code…

You could “censor” irrelevant parts of your code (like is done here). For this problem, however, the problem is visible from the behavior of your function on different values of n (I’d show 2-5 for clarity).

Also, note that any code that’s been git pushed is visible to TAs.