I'm interested in using/overloading the "range step" operator (.. ..), but I can't for the life of me find out how to use it.
In the documentation it says
// Usage:
start .. step .. finish
but trying that in the F# shell gives errors:
> let x = 1 .. 2 .. 7;;
let x = 1 .. 2 .. 7;;
----------^^
stdin(54,11): error FS0010: Unexpected symbol '..' in binding. Expected incomplete structured construct at or before this point or other token.
However, calling it "explicitly" is possible:
> let x = (.. ..) 1 2 7;;
val x : seq<int>
Is it only possible to use this operator for list/seq construction such as [1..2..7]
and seq {1..2..7}
?
The uses of this operator are covered by section 6.3.12 of the spec (Range Expressions). The built-in (.. ..)
operator works on any type with appropriate (+)
and Zero
members, but you can redefine it to do something else (note that this example is nonsensical):
let (.. ..) x y z =
Seq.map (fun (s:string) -> s.[z] + y) x
let result = seq { ["test"; "func"] .. (char 1) .. 2 } // contains 't' 'o'
Overriding (.. ..) operator
If you redefine (.. ..)
operator as in @kvb's answer, it will override that operator of any type. Since you probably want to make (.. ..)
operator work for a custom datatype, overriding static (+)
and One
members is enough. For example, here's a custom numeric type for modular arithmetic taken from @Tomas's blog:
type IntegerZ5 =
| Z5 of int
member z.ToInt32() =
let (Z5 n) = z in n
override z.ToString() =
sprintf "%d (mod 5)" (z.ToInt32())
static member Create(n) =
let z5 = n % 5
Z5(max ((z5 + 5) % 5) z5)
static member (+) (Z5 a, Z5 b) = IntegerZ5.Create(a + b)
static member (-) (Z5 a, Z5 b) = IntegerZ5.Create(a - b)
static member (*) (Z5 a, Z5 b) = IntegerZ5.Create(a * b)
static member Zero = Z5 0
static member One = Z5 1
let inline z5 a = IntegerZ5.Create(a)
While constructing a range starting from the lower bound, (+)
and One
are used to find the next element. The construction finishes when the next element equals to or exceeds the range's upper bound. Now you can employ IntegerZ5
in any range expression:
let s1 = seq{z5 37..z5 3};; // seq [Z5 2; Z5 3]
let s2 = seq{z5 10..z5 22..z5 4};; // seq [Z5 0; Z5 2; Z5 4]
Using (.. ..) operator
Another use of range expression is in for
loops. I find it helpful in many cases:
let sum =
let mutable s = 0L
for i in 1L..1000L do (* or 1L..1L..1000L with an explicit step *)
s <- s + i
s
because it is more flexible than for...to..do
which is restricted to int
only and implies a range with a step of 1
:
let sum =
let mutable s = 0L
for i = 1L to 1000L do (* doesn't work *)
s <- s + i
s
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