我有兴趣使用/重载“范围步骤”操作符(.. ..),但我不能为我的生活找到如何使用它。
在文档中说
// Usage:
start .. step .. finish
但是在F#shell中尝试它会产生错误:
> 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.
但是,可以“明确地”调用它:
> let x = (.. ..) 1 2 7;;
val x : seq<int>
是否只能将此运算符用于列表/序列构造,例如[1..2..7]
和seq {1..2..7}
?
答案 0 :(得分:2)
section 6.3.12 of the spec(范围表达式)涵盖了此运算符的用法。内置的(.. ..)
运算符适用于具有相应(+)
和Zero
成员的任何类型,但可以重新定义它以执行其他操作(请注意,此示例是荒谬的):
let (.. ..) x y z =
Seq.map (fun (s:string) -> s.[z] + y) x
let result = seq { ["test"; "func"] .. (char 1) .. 2 } // contains 't' 'o'
答案 1 :(得分:2)
覆盖(.. ..)运算符
如果你在@ kvb的答案中重新定义(.. ..)
运算符,它将覆盖任何类型的运算符。由于您可能希望(.. ..)
运算符适用于自定义数据类型,因此覆盖静态(+)
和One
成员就足够了。例如,这是从@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)
在构建从下限开始的范围时,(+)
和One
用于查找下一个元素。当下一个元素等于或超过范围的上限时,构造结束。现在,您可以在任何范围表达式中使用IntegerZ5
:
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]
使用(.. ..)运算符
范围表达式的另一个用途是for
循环。我发现它在很多情况下都很有用:
let sum =
let mutable s = 0L
for i in 1L..1000L do (* or 1L..1L..1000L with an explicit step *)
s <- s + i
s
因为它比仅限for...to..do
的{{1}}更灵活,并且暗示步长为int
的范围:
1
答案 2 :(得分:1)
spec § 6.3.12没有明确说明,但给出的唯一例子是在序列表达式中。这一点,以及没有其他工作的事实,似乎证实了你的结论。
仅供参考 - 以下是范围运营商的相关文档。
Operators.( .. )<^T>
Operators.( .. .. )<^T,^Step>
Section 3.8.2还提到(.. ..)
的特殊处理,因此可以安全地假设它受限于典型函数/运算符之外的限制/行为。