Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are simple for loop expressions restricted to integer ranges?

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?

like image 802
Frank Avatar asked Apr 16 '13 19:04

Frank


People also ask

Can you use for loop with integers?

One of the most common uses of for-loops in Python is to iterate over an interval of integers.

What are the expressions of a for loop?

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).

Does C have range-based for loops?

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.

What is the difference between a traditional for loop and a for in on Iterables?

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.


Video Answer


2 Answers

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).

like image 66
Tomas Petricek Avatar answered Sep 23 '22 07:09

Tomas Petricek


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
like image 20
JDB Avatar answered Sep 23 '22 07:09

JDB