将递归限制为浮动列表的前三个元素

Limit recursion to the first three elements of list of floats

我是函数式编程 (ReasonML / OCaml) 的新手。

我有一个花车清单。我想获得列表的前三个非零项,仅此而已。项目可以是正数、负数和零。

在提取前三个非零浮点数之前如何限制递归?

我正在考虑做类似的事情:

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

但是我如何保证firstsecondthird是非零​​浮点数呢?如果是,递归地丢弃它们,直到找到三个非零浮点数——或 return 0.0.

知道了!

List.filer可用于过滤掉等于零的元素。

let filtered = List.filter (fun item => item != 0.0) list;
switch (filtered) {
  | [first, second, third, ...rest] => (first +. second +. third) /. 3.0
  | _ => 0.0
};

使用递归执行此操作的更好方法是使其更通用:让我们找到 n 第一个非零值,而不是找到第 3 个非零值。

下面是一个OCaml实现,我不知道原因。

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)

函数签名如下:

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

这里发生了很多事情,但还不错。这里的逻辑如下:

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.

其他一切都只是额外的逻辑:

  • 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.

关于尾递归的一句话

不幸的是,上面的实现不是tail-recursive,这意味着它会在大型列表上失败。使函数尾递归的常用技术是使用累加器(下面代码中的acc)来存储中间结果。

然后,我们将这个额外的逻辑封装在本地辅助函数(通常称为 loopaux)中,这样累加器就不会出现在函数的签名中。

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 []