16

根据 F# 规范(参见§6.5.7),简单的 for 循环受整数(int又名int32aka System.Int32)限制startstop,例如

for i = start to stop do
    // do sth.

我想知道为什么这种类型的 for 循环的迭代边界必须是int32. 为什么不允许uint32int64? bigint?

我知道序列迭代表达式(for ... in ...)可以迭代任意序列;然而,这需要分配一个迭代器并调用MoveNextandCurrent什么不是,因此可能比普通循环效率低得多(增量计数器,比较,条件跳转)。为避免这种情况,您不得不使用while和手动递增循环计数器......

奇怪的是,如果表达式包含在序列表达式中,F#确实允许非int32循环边界,例如for

seq { for i = 0I to 10I do
        printfn "%A" i }

所以,我想问题是:是否有特殊原因只允许int32循环?为什么这个限制不适用于包含在表达式中的for循环?seq

4

3 回答 3

7

我不确定为什么 F# 不允许int64范围。这听起来像是一个有用的功能......(但我可以理解这int是 C# 中的标准类型,也许 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# 编译器能够相当不错地优化这一点(lambda 函数是内联的,尾递归loop函数变成了一个while循环)。我不认为这是有保证的,因此您可能需要在高性能代码中手动检查这一点,但对于更简单的示例似乎可以正常工作。

只有一个缺点 - 您将无法使用局部可变变量 ( let mutable),因为 lambda 函数无法捕获这些变量。所以间接单元可能会有额外的成本ref(但我不确定这是多大的问题)。

于 2013-04-17T00:34:56.750 回答
2

如果要保留 for 循环,可以使用带有序列范围运算符的for...in循环进行非常简单的解决方法:

for i in 0I .. 10I do
    printfn "%A" i

只要两种类型匹配,范围运算符将接受任何大小的任何整数。例如,以下将无法编译:

for i in 0 .. 10I do
    printfn "%A" i
于 2014-01-22T19:50:15.250 回答
0

另一种可能的解决方法:

[1L..100L] |> List.iter (fun i -> printfn "%i" i)

于 2013-04-17T11:06:50.163 回答