Как вернуть индекс цикла for в OCaml?
let find_free_next heap start =
for i = start to ((Array.length heap)-1) do
match heap.(i) with
Hdr (Free (h), g) ->
i
done
Как я могу вернуть индекс цикла в виде целого числа после того, как совпадение было найдено?
4 ответа
Если вы хотите придерживаться императивного стиля, вы можете использовать исключение для выхода из цикла:
exception Found of int
let find_free_next heap start =
try
for i = start to Array.length heap - 1 do
match heap.(i) with
| Hdr (Free (h), g) -> raise (Found i)
| _ -> () (* If it is not what you are seeking *)
done;
raise Not_found
with
| Found n -> n
Но в целом, как уже писал ppl, функциональный стиль в OCaml более предпочтителен:
let find_free_next heap start =
let len = Array.length heap in
let rec find i =
if i >= len then None
else
match heap.(i) with
| Hdr (Free h, g) -> Some i
| _ -> find (i+1)
in
find start
В этом примере между двумя версиями нет большой разницы, но использование исключений для выхода из циклов / рекурсий следует использовать с осторожностью; с ними можно довольно легко вносить ошибки потока управления, а иногда их сложно отлаживать.
Кстати, вы можете использовать Array.unsafe_get heap i для ускорения доступа к вашему массиву, поскольку вы можете быть уверены, что i всегда находится в допустимом диапазоне массива в приведенных выше примерах. (О, нам нужно дополнительно проверить начало>= 0.)
Проще и эффективнее (без выделения):
let rec find_free_next heap start =
if start = Array.length heap then raise Not_found;
match heap.(i) with
| Hdr (Free h, g) -> i
| _ -> find_free_start heap (i+1)
Или в императивном стиле:
let exit = Exit
let find_free_next heap start =
let pos = ref (-1) in
try
for i = start to Array.length heap - 1 do
match heap.(i) with
| Hdr (Free h, g) -> pos := i; raise exit
| _ -> ()
done;
raise Not_found
with Exit -> !pos
(заметить, что raise exit
не выделяет только потому, что исключение, если предварительно вычислено).
Асуму Такикава прав, for
цикл в OCaml не возвращает результат. В идиоматическом OCaml вы должны использовать рекурсию вместо этого. В идеале была бы стандартная функция, такая как List.find
это работает для массивов. Есть функция BatArray.findi
в комплект батарей OCaml входит то, что вы, кажется, хотите.
Циклы в Ocaml должны быть обязательными, поэтому они не должны возвращать результат (кроме единицы измерения). Поэтому, если вы попытаетесь вернуть результат, не связанный с единицей, компилятор выдаст предупреждение.
Причина, по которой Ocaml не позволяет вам возвращать результат из цикла, заключается в том, что это не очень функциональная идиома. Если вы используете рекурсивную функцию вместо цикла, вы можете легко выйти раньше и вернуть результат (возвращая результат вместо повторения). Если вы хотите написать идиоматический Ocaml, вы, вероятно, хотите использовать рекурсию в этом случае.