2017-08-07 2 views
4
let inline (=~) a b = abs (single a - single b) <= 0.001f 

type Vector = 
    { x : single; y : single; z : single } 

    static member (=~) (v1, v2) = (v1.x =~ v2.x) && (v1.y =~ v2.y) && (v1.z =~ v2.z) 

let v1, v2 = 
    { x = 0.1f; y = single Math.PI; z = 0.f }, 
    { x = 0.1f; y = 3.14159f; z = 0.0001f } 

v1 =~ v2 
Lösung

Compiler beklagt: The type 'Vector' does not support a conversion to the type 'single'F # Bediener nicht richtig

Ich verstehe es nicht. Offensichtlich hat der typspezifische Operator keinen Vorrang vor dem generischen Operator, was meine Intuition vereitelt. Was ist der Trick, um das zu schaffen?

Antwort

4

Wenn Sie einen benutzerdefinierten Operator mit let definieren, hat dieser immer Vorrang vor typdefinierten Operatoren. Der einfache Weg, dies zu umgehen, besteht darin, Namenskonflikte in lokalen und globalen Betreibernamen zu vermeiden oder den Umfang der an Operatoren gebundenen Operatoren auf ein Minimum zu beschränken. Zum Beispiel könnten Sie den globalen =~ Operator in einem separaten Modul setzen:

module VectorImplementation = 
    let inline (=~) a b = abs (single a - single b) <= 0.001f 

module Vectors = 
    open VectorImplementation 
    type Vector = 
     { x : single; y : single; z : single } 
     static member (=~) (v1, v2) = 
     (v1.x =~ v2.x) && (v1.y =~ v2.y) && (v1.z =~ v2.z) 

open System 
open Vectors 

let v1, v2 = 
    { x = 0.1f; y = single Math.PI; z = 0.f }, 
    { x = 0.1f; y = 3.14159f; z = 0.0001f } 

v1 =~ v2 

Es gibt auch ein somewhat strange hack, die Sie global let -bound Operatoren zu definieren, die überlastet sind verwenden können. Es gibt eine Debatte darüber, ob das eine gute Idee ist oder nicht - ich denke, dass es normalerweise möglich ist, Zusammenstöße zu vermeiden, ohne auf diesen magischen Trick zurückzugreifen, aber andere könnten dem widersprechen.

2

Seite 97 der F# 4.0 language specification sagt, dass:

Wenn der Betreiber nicht zu einem benutzerdefinierten oder Bibliothek definierte Betreiber sicherzustellen, löst die Namensauflösung Regeln (§14.1), dass der Bediener ein beschließt Ausdruck, der implizit einen statischen Aufrufaufruf (§0) verwendet, der die Typen der Operanden beinhaltet. Dies bedeutet, dass das effektive Verhalten eines Operators, der nicht in der F # -Bibliothek definiert ist, einen statischen Member erfordert, der denselben Namen wie der Operator hat, und zwar nach dem Typ eines Operanden des Operators.

Als Tomas Petricek hat gerade in seiner Antwort darauf hingewiesen, dass bedeutet, dass der =~ Operator global definiert wird, um den =~ Operator auf Vector Typ „versteckt“. Seine Antwort schlägt einen guten Ansatz vor, um mit dem Problem umzugehen. Eine andere Möglichkeit wäre, einfach die beiden Betreiber machen verschiedene:

let inline (==~) a b = abs (single a - single b) <= 0.001f 
type Vector = 
    { x : single; y : single; z : single } 
    static member (=~) (v1, v2) = 
     (v1.x ==~ v2.x) && (v1.y ==~ v2.y) && (v1.z ==~ v2.z) 

Sie können diese Vorgehensweise finden einfacher als Tomas Ansatz, oder Sie können seinen Ansatz einfacher zu finden. Es ist eine Frage der Stilpräferenz; entweder sollte man arbeiten.

Verwandte Themen