Reputation: 1
I have a problem with the following assignment:
The function fold_left can be used to implement many operations that scan through a list from left to right. It takes three arguments: a function f, an initial accumulator, and a list. For each element x in the list, the currect accumulator is combined with x to produce the next accumulator value; the result of fold_left is the final accumulator value. Consult the lecture slides or the documentation for the precise definition!
the task is the follwoing: we have to implement the function f acc v so that it will have all elements with an even index on the left side and all elements with an uneven index on the right side. with the element with the index 0 being in the middle: something like this: [an; ...; a0; an-1]
i have the following function which does not pass hidden tests..but i sadly have no idea what my method does not cover:
let f acc v =
if List.length acc = 0 then
acc @[v]
else if List.length acc mod 2 = 0 then
v :: acc
else List.length acc mod 2 = 0 then
acc @ [v]
Upvotes: 0
Views: 165
Reputation: 36536
As you go through a fold, your accumulator is the only state you have. It's the only way your function has any clue about what's going on. Choosing the type of that accumulator is key.
So, what do you need to know as you go through your fold?
0
.0
.But Chris! You can only pass one accumulator value, and that's four values! Can't you count?
No one said the one value couldn't be a tuple or a list containing multiple values.
Consider the following, which separates out even and odd indexed elements. Some elements of this sample replaced with ...
to prevent just completely providing a copy and paste solution.
# List.fold_left
(fun (i, evens, odds) x ->
if i mod 2 = 0 then (i+1, ..., ...)
else (i+1, ..., ...))
(0, [], [])
[1; 2; 3; 4; 5; 6];;
- : int * int list * int list = (6, [5; 3; 1], [6; 4; 2])
Your proposed solution using the length of the accumulator and either consing or appending could work, but as noted in comments, you have a syntax error. Your final comparison is unnecessary. You know that List.length acc
is not 0
and not evenly divisible by 2
. There is only one remaining state it could be in.
# let f acc v =
if List.length acc = 0 then
acc @ [v]
else if List.length acc mod 2 = 0 then
v :: acc
else
acc @ [v];;
val f : 'a list -> 'a -> 'a list = <fun>
# List.fold_left f [] [1;2;3;4;5;6];;
- : int list = [5; 3; 1; 2; 4; 6]
The inefficiency comes from how lists work in OCaml. They're singly linked. The only way to calculate the length or to append to the end of a list is to walk the entire list. You do that (potentially multiple times) on each iteration of your fold. The fold is O(n) so doing an O(n) operation on each iteration means the whole thing is O(n^2). For a list with a few elements? No big deal. Now imagine one of the test cases has 10,000 elements. Suddenly you have a problem, because 10,000 squared is 100,000,000. Even if I have to do an O(n) operation several times on a 10,000 element list, it'll never take nearly as long.
Now imagine 10,000,000 elements in the list. Squaring that is 100,000,000,000,000. It might look like your computer is simply frozen.
Upvotes: 3