Почему простые выражения цикла ограничены целыми диапазонами?

Согласно спецификации 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)