Почему простые выражения цикла ограничены целыми диапазонами?
Согласно спецификации F # (см. §6.5.7), простые для циклов ограничены целым числом (int
aka int32
aka System.Int32
) start
и stop
, например
for i = start to stop do
// do sth.
Интересно, почему ограничения итерации для этого типа цикла for должны быть int32
. Почему бы не позволить uint32
? int64
? bigint
?
Я знаю, что итерационные выражения последовательности (for ... in ...
) могут выполнять итерацию по произвольным последовательностям; что, однако, требует выделения итератора и вызова MoveNext
и Current
, а что нет и может быть значительно менее эффективным, чем простой цикл (приращение счетчика, сравнение, переход по условию). Чтобы этого избежать, вы застряли с использованием while
и счетчиков циклов с добавлением вручную...
Как ни странно, F # разрешает ограничения на int32
, если выражение for
обернуто в выражение последовательности, например
seq { for i = 0I to 10I do
printfn "%A" i }
Итак, я думаю, вопрос в том, есть ли конкретная причина только для того, чтобы разрешать int32
для циклов? И почему это ограничение не относится к циклам for
, заключенным в выражения seq
?
Ответы
Ответ 1
Я не уверен, почему F # не разрешает диапазоны int64
. Это звучит как полезная функция... (но я понимаю, что int
является стандартным типом для этого в С#, и, возможно, F # пытается следовать этому шаблону).
Что касается обходных решений, стоит добавить, что вы также можете написать inline
функцию более высокого порядка:
let inline longFor low high f =
let rec loop n =
if n < high then f n; loop (n + 1L)
loop low
... и затем вы можете выразить for
петли над диапазонами int64
довольно кратким образом:
longFor 1L 100L (fun n ->
<whatever> )
Я сделал несколько экспериментов, и кажется, что компилятор F # способен оптимизировать это довольно прилично (функция лямбда встроена, а функция tail-recursive loop
превращается в цикл while
). Я не думаю, что это гарантировано, поэтому вам может потребоваться проверить это вручную в высокопроизводительном коде, но, похоже, он работает отлично для более простых примеров.
Существует только один недостаток - вы не сможете использовать локальные изменяемые переменные (let mutable
), поскольку они не могут быть захвачены с помощью лямбда-функции. Таким образом, могут быть дополнительные затраты с косвенными ячейками ref
(но я не уверен, насколько это значительная проблема).
Ответ 2
Если вы хотите сохранить for-loop, там очень просто работать, используя для... в петле с оператор диапазона последовательности:
for i in 0I .. 10I do
printfn "%A" i
Оператор диапазона принимает любое целое число любого размера, если совпадают оба типа. Например, следующее не будет компилироваться:
for i in 0 .. 10I do
printfn "%A" i
Ответ 3
Еще одно возможное обходное решение:
[1L..100L] | > List.iter(fun я → printfn "% i" i)