我如何从在F#中迭代for循环的函数返回一个值

Cha*_*nya 4 f# loops functional-programming

我正在尝试循环数组并返回一个值,如下所示.但是在if语句之后,这给了我一个错误.它说"这个表达式应该有类型单位但是有类型int"

let findMostSignificantBitPosition (inputBits:System.Collections.BitArray) =
for i = inputBits.Length - 1 to 0  do
    if inputBits.[i] then
        i
done
Run Code Online (Sandbox Code Playgroud)

我该怎么做?我正在使用递归循环重新编码它,因为它似乎是在函数式语言中执行此类循环的更可接受的方式,但我仍然想知道上面我做错了什么.

gas*_*che 8

for循环不应该返回值,它们只执行固定次数的操作然后返回()(单位).如果你想迭代并最终返回一些东西,你可以:

  • 在循环之外有一个引用,当你得到最终结果时,然后在循环返回引用内容之后

  • 直接使用递归函数

  • 使用一个更高阶的函数来封装遍历,让你专注于应用程序逻辑

如果您的数据结构支持它,则更高的功能很好.fold_left但是,简单的遍历函数不支持过早地停止迭代.如果您希望支持这一点(显然在您的用例中会很有用),您必须使用具有过早退出支持的遍历.对于像你这样的简单函数,简单的递归函数可能是最简单的.

在F#中,还应该可以用命令式编写函数,使用yield它将其转换为生成器,然后最终强制生成器获得结果.这可以看作是使用异常跳出循环的OCaml技术的对应物.

编辑:避免"过早停止"问题的一个很好的解决方案是使用一个懒惰的中间数据结构,它只能构建到第一个令人满意的结果.这是优雅而优秀的脚本风格,但仍然比直接退出支持或简单递归效率低.我想这取决于你的需求; 这个函数用于关键路径吗?

编辑:以下是一些代码示例.它们是OCaml,数据结构不同(其中一些使用电池库),但想法是一样的.

(* using a reference as accumulator *)
let most_significant_bit input_bits =
  let result = ref None in
  for i = Array.length input_bits - 1 downto 0 do
    if input_bits.(i) then
      if !result = None then
        result := Some i
  done;
  !result

let most_significant_bit input_bits =
  let result = ref None in
  for i = 0 to Array.length input_bits - 1 do
    if input_bits.(i) then
      (* only the last one will be kept *)
      result := Some i
  done;
  !result

(* simple recursive version *)
let most_significant_bit input_bits =
  let rec loop = function
    | -1 -> None
    | i ->
      if input_bits.(i) then Some i
      else loop (i - 1)
  in
  loop (Array.length input_bits - 1)

(* higher-order traversal *)
open Batteries_uni
let most_significant_bit input_bits =
  Array.fold_lefti
    (fun result i ->
      if input_bits.(i) && result = None then Some i else result)
    None input_bits

(* traversal using an intermediate lazy data structure 
   (a --- b) is the decreasing enumeration of integers in [b; a] *)
open Batteries_uni
let most_significant_bit input_bits =
  (Array.length input_bits - 1) --- 0
  |> Enum.Exceptionless.find (fun i -> input_bits.(i))

(* using an exception to break out of the loop; if I understand
   correctly, exceptions are rather discouraged in F# for efficiency
   reasons. I proposed to use `yield` instead and then force the
   generator, but this has no direct OCaml equivalent. *)
exception Result of int
let most_significant_bit input_bits =
  try
    for i = Array.length input_bits - 1 downto 0 do
      if input_bits.(i) then raise (Result i)
    done;
    None
  with Result i -> Some i
Run Code Online (Sandbox Code Playgroud)