Циклы
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