According to the F# spec (see §6.5.7), simple for loops are bounded by integer (int
aka int32
aka System.Int32
) limits start
and stop
, e.g.
for i = start to stop do
// do sth.
I wonder why the iteration bounds for this type of for loop are required to be int32
. Why not allow uint32
? int64
? bigint
?
I'm aware that sequence iteration expressions (for ... in ...
) can iterate over arbitrary sequences; that however requires allocating an iterator and calling MoveNext
and Current
and what not and can thus be considerably less efficient than a plain loop could be (increment counter, compare, conditonal jump). To avoid that, you are stuck with using while
and a manually incrementing loop counters...
Strangely enough, F# does allow non-int32
loop bounds, if the for
expression is wrapped in a sequence expression, e.g.
seq { for i = 0I to 10I do
printfn "%A" i }
So, I guess the question is: Is there a particular reason for only allowing int32
for loops? And why does this restriction not apply to for
loops wrapped in seq
expressions?
One of the most common uses of for-loops in Python is to iterate over an interval of integers.
For Loop Expressions are a way to use a regular for Loop as part of an expression, symmetrical to the new Case Expressions and If Expressions. The result of the for loop expression is a Sequence of values (implemented internally the same way as an Iterator would be).
Range-based for loop in C++ It executes a for loop over a range. Used as a more readable equivalent to the traditional for loop operating over a range of values, such as all elements in a container.
They are basically the same, but for-each (the second one) has certain restrictions. It can be used for accessing the array elements but not for modifying them. It is not usable for loops that must iterate over multiple collections in parallel—for example, to compare the elements of two arrays.
I'm not sure why F# does not allow int64
ranges. It sounds like a useful feature... (but I can understand that int
is the standard type for this in C# and perhaps F# tries to follow this pattern).
As for the workarounds, it is worth adding that you can also write inline
higher-order function:
let inline longFor low high f =
let rec loop n =
if n < high then f n; loop (n + 1L)
loop low
...and then you can express for
loops over int64
ranges in a fairly succinct way:
longFor 1L 100L (fun n ->
<whatever> )
I did a couple of experiments and it seems that the F# compiler is able to optimize this fairly decently (the lambda function is inlined and the tail-recursive loop
function is turned into a while
loop). I do not think this is guaranteed so you may need to check this by hand in high-performance code, but it seems to work fine for simpler examples.
There is only one disadvantage - you won't be able to use local mutable variables (let mutable
) because these cannot be captured by a lambda function. So there may be additional cost with indirect ref
cells (but I'm not sure how big problem this is).
If you want to keep the for-loop, there's a very simple work-around using the for...in loop with a sequence range operator:
for i in 0I .. 10I do
printfn "%A" i
The range operator will accept any integer of any size as long as both types match. For example, the following will not compile:
for i in 0 .. 10I do
printfn "%A" i
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With