Megosztás a következőn keresztül:


Operátor túlterhelése

Ez a témakör azt ismerteti, hogyan lehet túlterhelni az aritmetikai operátorokat egy osztály- vagy rekordtípusban, valamint globális szinten.

Syntax

// Overloading an operator as a class or record member.
static member (operator-symbols) (parameter-list) =
    method-body
// Overloading an operator at the global level
let [inline] (operator-symbols) parameter-list = function-body

Megjegyzések

Az előző szintaxisban az operátorszimbólum az egyik +, -, *, /, =stb. A paraméterlista az operandusokat az operátor szokásos szintaxisában megjelenő sorrendben adja meg. A metódus törzse az eredményként kapott értéket hozza létre.

Az operátorok túlterhelésének statikusnak kell lennie. Az operátor túlterheltségéhez az +-operátorszimbólumbanegy tilde (~) karaktert kell használnia, amely jelzi, hogy az operátor nem bináris operátor, hanem nem bináris operátor, ahogyan az az alábbi deklarációban is látható.

static member (~-) (v : Vector)

Az alábbi kód egy olyan vektorosztályt mutat be, amely csak két operátort, egyet a unary mínuszhoz, egyet pedig egy skaláris szorzáshoz. A példában két túlterhelésre van szükség a skaláris szorzáshoz, mert az operátornak a vektor és a skaláris megjelenési sorrendtől függetlenül kell működnie.

type Vector(x: float, y : float) =
   member this.x = x
   member this.y = y
   static member (~-) (v : Vector) =
     Vector(-1.0 * v.x, -1.0 * v.y)
   static member (*) (v : Vector, a) =
     Vector(a * v.x, a * v.y)
   static member (*) (a, v: Vector) =
     Vector(a * v.x, a * v.y)
   override this.ToString() =
     this.x.ToString() + " " + this.y.ToString()

let v1 = Vector(1.0, 2.0)

let v2 = v1 * 2.0
let v3 = 2.0 * v1

let v4 = - v2

printfn "%s" (v1.ToString())
printfn "%s" (v2.ToString())
printfn "%s" (v3.ToString())
printfn "%s" (v4.ToString())

Új operátorok létrehozása

Az összes standard operátort túlterhelheti, de bizonyos karakterek sorozatából is létrehozhat új operátorokat. Az engedélyezett operátorkarakterek a következők!: , $, %, &, <-.=>/+*, ?, @, ^|, és .~ A ~ karakter különleges jelentése, hogy egy operátort el nem kell különíteni, és nem része az operátor karaktersorozatának. Nem minden operátor tehető elártalaníthatatlanné.

A használt karaktersorozattól függően az operátornak bizonyos elsőbbsége és asszociativitása lesz. Az asszociativitás lehet balról jobbra vagy jobbról balra, és akkor használatos, ha az azonos szintű elsőbbséget élvező operátorok zárójelek nélkül jelennek meg sorrendben.

Az operátor karaktere . nem befolyásolja az elsőbbséget, így például ha a szorzás saját verzióját szeretné definiálni, amely a szokásos szorzáséval azonos előzményekkel és asszociativitásokkal rendelkezik, létrehozhat például..*

Az $ operátornak önállóan és további szimbólumok nélkül kell állnia.

Az F# összes operátorának elsőbbségét megjelenítő táblázat a Szimbólum és az Operátor referenciában található.

Túlterhelt operátornevek

Amikor az F#-fordító egy operátorkifejezést fordít le, létrehoz egy metódust, amelynek fordító által generált neve van az adott operátornak. Ez az a név, amely a metódus közös köztes nyelvében (CIL) jelenik meg, valamint a tükröződésben és az IntelliSense-ben is. Ezeket a neveket általában nem kell F#-kódban használnia.

Az alábbi táblázat a standard operátorokat és a hozzájuk tartozó generált neveket mutatja be.

Operátor Létrehozott név
[] op_Nil
:: op_Cons
+ op_Addition
- op_Subtraction
* op_Multiply
/ op_Division
@ op_Append
^ op_Concatenate
% op_Modulus
&&& op_BitwiseAnd
||| op_BitwiseOr
^^^ op_ExclusiveOr
<<< op_LeftShift
~~~ op_LogicalNot
>>> op_RightShift
~+ op_UnaryPlus
~- op_UnaryNegation
= op_Equality
<= op_LessThanOrEqual
>= op_GreaterThanOrEqual
< op_LessThan
> op_GreaterThan
? op_Dynamic
?<- op_DynamicAssignment
|> op_PipeRight
<| op_PipeLeft
! op_Dereference
>> op_ComposeRight
<< op_ComposeLeft
<@ @> op_Quotation
<@@ @@> op_QuotationUntyped
+= op_AdditionAssignment
-= op_SubtractionAssignment
*= op_MultiplyAssignment
/= op_DivisionAssignment
.. op_Range
.. .. op_RangeStep

Vegye figyelembe, hogy az not F# operátora nem bocsát ki op_Inequality , mert nem szimbolikus operátor. Ez egy függvény, amely egy logikai kifejezést tagadó IL-t bocsát ki.

Az itt nem felsorolt operátorkarakterek egyéb kombinációi operátorként is használhatók, és az alábbi táblázat egyes karaktereinek összefűzésével létrehozott nevekből állnak. Például: +! lesz op_PlusBang.

Operátor karakter Név
> Greater
< Less
+ Plus
- Minus
* Multiply
/ Divide
= Equals
~ Twiddle
$ Dollar
% Percent
. Dot
& Amp
| Bar
@ At
^ Hat
! Bang
? Qmark
( LParen
, Comma
) RParen
[ LBrack
] RBrack

Előtag és infix operátorok

Az előtag operátorai várhatóan egy operandus vagy operandus elé kerülnek, hasonlóan egy függvényhez. Az infix operátorok várhatóan a két operandus között lesznek elhelyezve.

Előtag-operátorként csak bizonyos operátorok használhatók. Egyes operátorok mindig előtag operátorok, mások lehetnek infixek vagy előtagok, a többi pedig mindig infix operátor. A kivétellel !!=kezdődő operátorok és az operátorok ~vagy ismétlődő sorozatok mindig előtag-operátorok~. Az operátorok +, -, +., -., &, &&, , , %és %% lehetnek előtag operátorok vagy infix operátorok. Az operátorok előtagverzióját úgy különböztetheti meg az infix verziótól, hogy a definiáláskor hozzáad egy ~ előtag-operátor elejéhez. Az ~ operátor használata nem használható, csak akkor, ha meg van adva.

Példa

Az alábbi kód bemutatja, hogy az operátorok túlterhelése egy törttípus implementálásához használható. A törteket számláló és nevező jelöli. A függvény hcf a legnagyobb gyakori tényező meghatározására szolgál, amely a törtrészek csökkentésére szolgál.

// Determine the highest common factor between
// two positive integers, a helper for reducing
// fractions.
let rec hcf a b =
  if a = 0u then b
  elif a<b then hcf a (b - a)
  else hcf (a - b) b

// type Fraction: represents a positive fraction
// (positive rational number).
type Fraction =
   {
      // n: Numerator of fraction.
      n : uint32
      // d: Denominator of fraction.
      d : uint32
   }

   // Produce a string representation. If the
   // denominator is "1", do not display it.
   override this.ToString() =
      if (this.d = 1u)
        then this.n.ToString()
        else this.n.ToString() + "/" + this.d.ToString()

   // Add two fractions.
   static member (+) (f1 : Fraction, f2 : Fraction) =
      let nTemp = f1.n * f2.d + f2.n * f1.d
      let dTemp = f1.d * f2.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // Adds a fraction and a positive integer.
   static member (+) (f1: Fraction, i : uint32) =
      let nTemp = f1.n + i * f1.d
      let dTemp = f1.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // Adds a positive integer and a fraction.
   static member (+) (i : uint32, f2: Fraction) =
      let nTemp = f2.n + i * f2.d
      let dTemp = f2.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // Subtract one fraction from another.
   static member (-) (f1 : Fraction, f2 : Fraction) =
      if (f2.n * f1.d > f1.n * f2.d)
        then failwith "This operation results in a negative number, which is not supported."
      let nTemp = f1.n * f2.d - f2.n * f1.d
      let dTemp = f1.d * f2.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // Multiply two fractions.
   static member (*) (f1 : Fraction, f2 : Fraction) =
      let nTemp = f1.n * f2.n
      let dTemp = f1.d * f2.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // Divide two fractions.
   static member (/) (f1 : Fraction, f2 : Fraction) =
      let nTemp = f1.n * f2.d
      let dTemp = f2.n * f1.d
      let hcfTemp = hcf nTemp dTemp
      { n = nTemp / hcfTemp; d = dTemp / hcfTemp }

   // A full set of operators can be quite lengthy. For example,
   // consider operators that support other integral data types,
   // with fractions, on the left side and the right side for each.
   // Also consider implementing unary operators.

let fraction1 = { n = 3u; d = 4u }
let fraction2 = { n = 1u; d = 2u }
let result1 = fraction1 + fraction2
let result2 = fraction1 - fraction2
let result3 = fraction1 * fraction2
let result4 = fraction1 / fraction2
let result5 = fraction1 + 1u
printfn "%s + %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result1.ToString())
printfn "%s - %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result2.ToString())
printfn "%s * %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result3.ToString())
printfn "%s / %s = %s" (fraction1.ToString()) (fraction2.ToString()) (result4.ToString())
printfn "%s + 1 = %s" (fraction1.ToString()) (result5.ToString())

Kimeneti:

3/4 + 1/2 = 5/4
3/4 - 1/2 = 1/4
3/4 * 1/2 = 3/8
3/4 / 1/2 = 3/2
3/4 + 1 = 7/4

Operátorok globális szinten

Az operátorokat globális szinten is definiálhatja. Az alábbi kód egy operátort +?határoz meg.

let inline (+?) (x: int) (y: int) = x + 2*y
printf "%d" (10 +? 1)

A fenti kód kimenete a következő 12: .

A normál számtani operátorokat így újradefinihatja, mivel az F# hatókörkezelési szabályai azt diktálják, hogy az újonnan definiált operátorok elsőbbséget élveznek a beépített operátorok felett.

A kulcsszót inline gyakran használják globális operátorokkal, amelyek gyakran kis függvények, amelyek a legjobban integrálva vannak a hívókódba. Az operátorfüggvények beágyazottvá tétele lehetővé teszi, hogy statikusan feloldott típusparaméterekkel dolgozva statikusan feloldott általános kódot generáljanak. További információ: Beágyazott függvények és statikusan feloldott típusparaméterek.

Lásd még