(FS0193, FS1113) Класс F # Vector со статически разрешенными параметрами типа - PullRequest
5 голосов
/ 16 мая 2019

Я пытаюсь реализовать общий класс Vector< ^F> в F #, где ^F - это базовый тип поля элементов вектора.Это означает, что ^F может быть любым, что удовлетворяет сложению, вычитанию, умножению и отрицанию.

Некоторые примеры кода пользователя могут выглядеть так:

    let v = Vector<int>([| 1; 2; 3 |])
    let w = Vector<int>([| 4; 5; 6 |])
    let sum = v + w

Так что здесь я использую тип int, и это может быть что-нибудь , которое удовлетворяет базовым базовым операциям, отмеченным выше.Кажется, у меня работает какая-то версия при использовании обобщений стиля .NET, но там у меня тоже проблемы.Так как я в любом случае хочу использовать SRTP, я снова пошел по этому пути:

    type Vector< ^F when ^F : (static member (~-): ^F -> ^F)
                     and ^F : (static member (+): ^F * ^F -> ^F)
                     and ^F : (static member (*): ^F * ^F -> ^F)
               >(_values: ^F[]) =
        let values: ^F [] = _values
        member inline this.Values = values

        member inline this.Dimension = Array.length values

        // Constructs a Vector using given initializer
        static member inline Init (n: int) (initializer: (int -> ^F)) =
            Vector< ^F>(Array.init n (fun i -> initializer (i + 1)))

        member inline this.Item with get (i: int) = values.[i - 1]

        // negate a vector
        static member inline ( ~- ) (a: Vector< ^F>) =
            Vector< ^F>.Init (Array.length a.Values) (fun i -> -a.[i])

        // sum of two vectors
        static member inline ( + ) (a: Vector< ^F>, b: Vector< ^F>): Vector< ^F> =
            Vector< ^F>.Init (Array.length a.Values) (fun i -> a.[i] + b.[i])

        // difference of two vectors
        static member inline ( - ) (a: Vector< ^F>, b: Vector< ^F>): Vector< ^F> =
            Vector< ^F>.Init (Array.length a.Values) (fun i -> a.[i] + (-b.[i]))

        // scale vector by scalar
        static member inline ( * ) (a: ^F, b: Vector< ^F>): Vector< ^F> =
            Vector< ^F>.Init (Array.length b.Values) (fun i -> a * b.[i])

Но ошибки, которые я получаю, утомляют мотивацию, например:

  • FS0193 (предупреждение): A type parameter is missing a constraint 'when ( ^F or ^?12844) : (static member ( + ) : ^F * ^?12844 -> ^F)' (но также для оператора - и *)
  • FS1113 (ошибка): The value 'Values' was marked inline but its implementation makes use of an internal or private function which is not - это в основном всеи сообщается для каждого свойства элемента, функции-члена и статической функции-члена.

(приведенный выше код совместим с copy'n'paste для простого воспроизведения)

Как мне решить проблему создания типа, такого как Vector, чьи элементы и операции имеют статически разрешенный параметр типа (или универсальный) без ошибок.

Ответы [ 2 ]

6 голосов
/ 16 мая 2019

Ошибки SRTP диагностировать ужасно, и считается ошибкой, что внутренне сгенерированный идентификатор типа просачивается в пространство пользователя.Это возможно не ясно с сообщением об ошибке, но статические члены не удовлетворяют всем ограничениям.Если вы выделите операторы в отдельный модуль, то вывод типа может позаботиться о применении всех ограничений (начиная с F # 4.6):

type Vector< ^F when ^F : (static member (~-): ^F -> ^F)
                and ^F : (static member (+): ^F * ^F -> ^F)
                and ^F : (static member (*): ^F * ^F -> ^F)
           >(_values: ^F[]) =
    let values: ^F [] = _values

    member inline __.Values = values
    member inline __.Dimension = Array.length values

    // Constructs a Vector using given initializer
    static member inline Init (n: int) (initializer: (int -> ^F)) =
        Vector< ^F>(Array.init n (fun i -> initializer (i + 1)))

    member inline __.Item with get (i: int) = values.[i - 1]

[<AutoOpen>]
module VectorOps =
    let inline ( ~- ) (v: Vector< ^F>) =
        Vector< ^F>.Init (Array.length v.Values) (fun i -> -v.[i])

    let inline ( + ) (a: Vector< ^F>)  (b: Vector< ^F>) =
        Vector< ^F>.Init (Array.length a.Values) (fun i -> a.[i] + b.[i])

    let inline ( - ) (a: Vector< ^F>)  (b: Vector< ^F>) =
        Vector< ^F>.Init (Array.length a.Values) (fun i -> a.[i] - b.[i])

    let inline ( * ) (k: ^F)  (v: Vector< ^F>) =
        Vector< ^F>.Init (Array.length v.Values) (fun i -> k * v.[i])

Затем вы можете использовать это, как и ожидалось:

let v1 = Vector<int>([|1;2;3;4;5|])
let v2 = Vector<int>([|1;2;3;4;5|])

let negv1 = -v1
negv1.Values |> Array.iter (fun x -> printfn "%d " x)

let sum  = v1 + v2
sum.Values |> Array.iter (fun x -> printfn "%d " x)

let diff = sum - v2
diff.Values |> Array.iter (fun x -> printfn "%d " x)

let scaled = 3 * diff
scaled.Values |> Array.iter (fun x -> printfn "%d " x)

К сожалению, это выходит на ту территорию, где вы сталкиваетесь с неприятными концами компилятора F #.SRTP не был специально разработан для этого стиля программирования, и, хотя он, безусловно, работает, отсутствие намерения для этого сценария действительно просачивается в причудах и сообщениях об ошибках.

3 голосов
/ 16 мая 2019

Я хочу предложить вам существующее решение для того, что вы хотите сделать.

Библиотека FSharpPlus предоставляет множество решений для последовательного решения этой проблемы.

Я думаю, что хорошей особенностью для этого типа векторов является Аппликативные математические операторы (см. Раздел «Использование аппликативных математических операторов»).

Вот пример с вашим кодом:

#r @"FSharpPlus.dll"
open FSharpPlus
open FSharpPlus.Data
open FSharpPlus.Math.Applicative

let v = ZipList [| 1; 2; 3 |]
let w = ZipList [| 4; 5; 6 |]
let sum = v .+. w

// val it : ZipList<int> = ZipList (seq [5; 7; 9])

let mul = v .*. w

// val it : ZipList<int> = ZipList (seq [4; 10; 18])

Вы также можете выполнять операции со скалярами:

v .* 6 ;;
// val it : ZipList<int> = ZipList (seq [6; 12; 18])

7 +. w ;;
val it : ZipList<int> = ZipList (seq [11; 12; 13])

Используемая здесь базовая абстракция: Applicative Functors , поскольку вектор (представьте ZipList как вектор) - это Applicative Functor, он просто работает.

ZipList, является немного стандартным в мире FP, но в качестве альтернативы вы также можете взглянуть на аппликатив ParallelArray . Если вам не нравятся имена, вы можете скопировать и вставить код и изменить определение на:

type TVector<'t> =
| Scalar of 't
| Vector of 't array
...
let v = Vector [| 1; 2; 3 |]
v .* 6 ;;
// or
v .*. Scalar 6

И, наконец, если вы не хотите использовать библиотеку, вы можете использовать ее как источник вдохновения и скопировать необходимый код. Это только там, и это работает.

Примечание

Что касается вашего исходного кода, если вы посмотрите на источники, на которые я указал, вы увидите, что он использует более функциональный подход и, например, он не ограничивает параметр типа на уровне типа, а ограничивает функции.

type Vector< ^F>(_values: ^F[]) =
     let values: ^F [] = _values
     member inline this.Values = values

     member inline this.Dimension = Array.length values

     // Constructs a Vector using given initializer
     static member inline Init (n: int) (initializer: (int -> 'f)) =
         Vector<'f>(Array.init n (fun i -> initializer (i + 1)))

     member inline this.Item with get (i: int) = values.[i - 1]


     // negate a vector
     static member inline ( ~- ) (a: Vector<'f>) =
         Vector<'f>.Init (Array.length a.Values) (fun i -> -a.[i])

Обратите внимание, что заглавная буква ˆF используется только на уровне типа, тогда как в функциях (или статических членах) используется другой параметр типа, строчные буквы 'f.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...