根据F#规范(参见§6.5.7),简单for循环以整数(int
又名int32
又名System.Int32
)限制start
和stop
,例如
for i = start to stop do
// do sth.
我想知道为什么这种类型的for循环的迭代界限必须是int32
。为什么不允许uint32
? int64
? bigint
?
我知道序列迭代表达式(for ... in ...
)可以迭代任意序列;然而,这需要分配一个迭代器并调用MoveNext
和Current
什么不是,因此可能比普通循环效率低得多(增量计数器,比较,条件跳转)。为避免这种情况,您将无法使用while
和手动递增循环计数器......
奇怪的是,如果int32
表达式包含在序列表达式中,F# 允许非for
循环边界,例如
seq { for i = 0I to 10I do
printfn "%A" i }
所以,我想问题是:是否有一个特殊的原因只允许int32
循环?为什么此限制不适用于for
表达式中包含的seq
循环?
答案 0 :(得分: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
单元格可能会产生额外成本(但我不确定这是多大的问题)。
答案 1 :(得分:2)
如果你想保留for循环,可以使用for...in循环sequence range operator进行非常简单的解决方法:
for i in 0I .. 10I do
printfn "%A" i
只要两种类型匹配,范围运算符将接受任何大小的任何整数。例如,以下将不编译:
for i in 0 .. 10I do
printfn "%A" i
答案 2 :(得分:0)
另一种可能的解决方法:
[1L..100L] |&gt; List.iter(有趣的i - &gt; printfn“%i”i)