0

I'm new to functional programming (ReasonML / OCaml).

I have a list of floats. I want to get the first three non-zero items of a list and no more. Items can be positive, negative, and zero.

How can recursion be limited until the first three non-zero floats are extracted?

I was thinking of doing something like:

switch (list) {
  | [first, second, third, ...rest] => (first +. second +. third) /. 3.0
  | _ => 0.0
};

But how can I guarantee that first, second, and third are non-zero floats? And if they are, discard them recursively until three non-zero floats are found -- or return 0.0.

Raphael Rafatpanah
  • 19,082
  • 25
  • 92
  • 158

2 Answers2

1

A nicer way to do this using recursion is to make it more generic: instead of finding the 3 first non-zero, let's find the n first non-zero.

Below is an OCaml implementation, I don't know Reason.

let rec find_n l n ~f =
  if n = 0 then []
  else match l with
  | [] -> failwith "Cannot find enough items"
  | h::t ->
      if f h then
        h :: (find_n t (n-1) ~f)
      else
        find_n (t n ~f)

Here's the function's signature:

val find_n : 'a list -> int -> f:('a -> bool) -> 'a list = <fun>

There's a lot going on here, but it's not that bad. The logic here is the following:

If I want 3 items from a list:

  • If its head matches, then I need to look for 2 items in the tail.
  • Else, then I still need to look for 3 items in the tail.

Everything else is just additional logic:

  • If I'm looking for 0 items, I'm done.
  • If the list is empty and I still need to look for more items, I failed.

A word about tail-recursion

Unfortunately, the above implementation is not tail-recursive, which means it will fail on large lists. A common technique to make a function tail-recursive is to use an accumulator (acc in the code below) to store the intermediate result.

Then, we encapsulate this extra logic in a local helper function (often called loop or aux) so that the accumulator doesn't show up in the function's signature.

let find_n l n ~f =
  let rec loop l' n' acc =
    if n' = 0 then acc else
      match l' with
      | [] -> failwith "Cannot find enough items"
      | h::t ->
          if f h then
            loop t (n' - 1) (h::acc)
          else
            loop t n' acc
  in
  loop l n []
Richard-Degenne
  • 2,892
  • 2
  • 26
  • 43
  • 1
    Thank you very much! For those that would like to see a ReasonML syntax, you can use this tool to convert between OCaml and ReasonML snippets. https://reasonml.github.io/try/?ocaml=DYUwLgBAZglgdgEwPpwsCqB+UIF4IGFHEmlnkWVXU2130ONMUBQBokATiAMZoD2-AA5oA5BnEBDHn1xtCMHHHH4ADBDAALEKml8QwAM4h5RALaSwPTWIgB3GFtOEAPhADaAXQgBaAHzQkjDADloQAEQAwpJwcPyQsIgQOvwArgDmNo4gZobhzgRumgBcxZD+BcSK0BA2WjqVJMCCIpAAFMq+EACMAJQQbSXFer2NhAbGY0TNwhoSEHry8PIzIuioXkA – Raphael Rafatpanah Sep 14 '17 at 14:41
0

Got it!

List.filer can be used to filter out elements equal to zero.

let filtered = List.filter (fun item => item != 0.0) list;
switch (filtered) {
  | [first, second, third, ...rest] => (first +. second +. third) /. 3.0
  | _ => 0.0
};
Raphael Rafatpanah
  • 19,082
  • 25
  • 92
  • 158