Использование оператора F # (.. ..) / перегрузка - PullRequest
5 голосов
/ 13 января 2012

Я заинтересован в использовании / перегрузке оператора "шаг шага по дальности" (.. ..), но пока не могу понять, как его использовать.

В документациион говорит

// Usage:
start .. step .. finish

, но попытка этого в оболочке F # приводит к ошибкам:

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

Isэтот оператор можно использовать только для построения списка / последовательности, таких как [1..2..7] и seq {1..2..7}?

Ответы [ 3 ]

2 голосов
/ 13 января 2012

Использование этого оператора охватывается разделом 6.3.12 спецификации (Выражения диапазона).Встроенный оператор (.. ..) работает с любым типом с соответствующими (+) и 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'
2 голосов
/ 13 января 2012

Оператор переопределения (.. ..)

Если переопределить оператор (.. ..), как в ответе @ kvb, он переопределит этот оператор любого типа.Поскольку вы, вероятно, хотите, чтобы оператор (.. ..) работал для пользовательского типа данных, достаточно переопределить статические члены (+) и One.Например, вот пользовательский числовой тип для модульной арифметики, взятый из @ блога Томаса :

  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, который ограничен только int и подразумевает диапазон с шагом 1:

let sum = 
    let mutable s = 0L
    for i = 1L to 1000L do (* doesn't work *)
        s <- s + i
    s
1 голос
/ 13 января 2012

spec § 6.3.12 не является явным в этом отношении, но единственные примеры приведены в выражениях последовательности. Это, наряду с тем фактом, что больше ничего не работает, похоже, подтверждает ваш вывод.

К вашему сведению - вот соответствующие документы для операторов диапазона.

Operators.( .. )<^T>
Operators.( .. .. )<^T,^Step>

В разделе 3.8.2 также упоминается специальная обработка (.. ..), поэтому можно с уверенностью предположить, что она подвержена ограничениям / поведению, помимо функций типичных функций / операторов.

...