Operator Kelebihan Beban

Topik ini menjelaskan cara memberi beban lebih pada operator aritmatika di kelas atau jenis rekaman, dan di tingkat global.

Sintaks

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

Keterangan

Dalam sintaks sebelumnya, simbol operator merupakan salah satu dari +, -, *, /, =, dan sebagainya. Daftar parameter menentukan operand dalam urutan kemunculannya di sintaks yang biasa untuk operator tersebut. Metode-isi membangun nilai yang dihasilkan.

Operator kelebihan beban untuk berbagai operator harus statis. Operator kelebihan beban untuk operator tunggal, seperti + dan -, harus menggunakan tilde (~) di simbol operator untuk menunjukkan bahwa operator adalah operator tunggal dan bukan merupakan operator biner, seperti yang ditunjukkan dalam deklarasi berikut.

static member (~-) (v : Vector)

Kode berikut mengilustrasikan kelas vektor yang hanya memiliki dua operator, satu untuk minus unary dan yang satu untuk perkalian dengan skalar. Dalam contoh, dua operator kelebihan beban untuk perkalian skalar diperlukan karena operator harus berfungsi terlepas dari urutan saat vektor dan skalar muncul.

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

Pembuatan Operator Baru

Anda dapat memberi beban lebih semua operator standar, tetapi Anda juga dapat membuat operator baru dari urutan karakter tertentu. Karakter operator yang diizinkan adalah !, $, %, &, *, +, -, ., /, <, =, >, ?, @, ^, |, dan ~. Karakter ~ ini memiliki arti khusus dalam pembuatan operator tunggal, dan bukan bagian dari urutan karakter operator. Tidak semua operator dapat dibuat tunggal.

Tergantung pada urutan karakter tepat yang Anda gunakan, operator Anda akan memiliki prioritas dan asosiatif tertentu. Asosiatif dapat berupa kiri ke kanan atau kanan ke kiri dan digunakan setiap kali operator dengan tingkat prioritas yang sama muncul secara berurutan tanpa tanda kurung.

Karakter operator . tidak memengaruhi prioritas, sehingga, misalnya, jika Anda ingin menentukan versi perkalian Anda sendiri yang memiliki prioritas dan asosiatif yang sama dengan perkalian biasa, Anda dapat membuat operator seperti .*.

Operator $ harus berdiri sendiri dan tanpa simbol tambahan.

Tabel yang memperlihatkan prioritas semua operator di F# dapat ditemukan di Referensi Simbol dan Operator.

Nama Operator Kelebihan Beban

Ketika kompilator F# melakukan kompilasi ekspresi operator, ia membuat metode yang memiliki nama yang dihasilkan kompilator untuk operator tersebut. Ini adalah nama yang muncul dalam bahasa perantara umum (CIL) untuk metode , dan juga dalam refleksi dan IntelliSense. Anda biasanya tidak perlu menggunakan berbagai nama ini dalam kode F#.

Tabel berikut menunjukkan operator standar dan nama yang dihasilkan terkait dengannya.

Operator Nama yang dihasilkan
[] 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

Perhatikan bahwa operator not di F# tidak memancarkan op_Inequality karena bukan merupakan operator simbolis. Ini adalah fungsi yang memancarkan IL (bahasa perantara) ekspresi boolean.

Kombinasi karakter operator lain yang tidak tercantum di sini dapat digunakan sebagai operator dan memiliki nama yang dibuat dengan melakukan penggabungan nama untuk karakter individual dari tabel berikut. Contohnya, +! op_PlusBangmenjadi .

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

Operator Awalan dan Infiks

Operator awalan diharapkan ditempatkan di depan sebuah operand atau beberapa operand, seperti fungsi. Operator infiks diharapkan ditempatkan di antara dua operand.

Hanya operator tertentu yang dapat digunakan sebagai operator awalan. Beberapa operator selalu merupakan operator awalan, yang lain dapat berupa infiks atau awalan, dan sisanya selalu operator infiks. Operator yang dimulai dengan !, kecuali !=, dan operator ~, atau urutan berulang ~, selalu merupakan operator awalan. Operator +, -, +., -., &, &&, %, dan %% dapat menjadi operator awalan atau operator infiks. Anda membedakan versi awalan dari berbagai operator ini dari versi infiks dengan menambahkan ~ di awal operator awalan saat operator tersebut ditentukan. ~ tidak digunakan saat Anda menggunakan operator, hanya ketika ditentukan.

Contoh

Kode berikut menggambarkan penggunaan operator kelebihan beban untuk mengimplementasikan jenis pecahan. Pecahan diwakili oleh numerator dan denominator. Fungsi hcf ini digunakan untuk menentukan faktor umum tertinggi, yang digunakan untuk mengurangi pecahan.

// 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())

Output:

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

Operator di Tingkat Global

Anda juga dapat menentukan operator di tingkat global. Kode berikut menentukan operator +?.

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

Output dari kode di atas adalah 12.

Anda dapat menentukan ulang operator aritmatika reguler dengan cara ini karena aturan cakupan untuk F# menentukan bahwa operator yang baru ditentukan lebih diprioritaskan daripada operator bawaan.

Kata kunci inline sering digunakan dengan operator global, yang sering kali merupakan fungsi kecil yang paling baik untuk diintegrasikan ke dalam kode panggilan. Membuat fungsi operator sebaris juga memungkinkannya untuk berfungsi dengan parameter jenis yang diselesaikan secara statis untuk menghasilkan kode generik yang diselesaikan secara statis. Untuk informasi selengkapnya, lihat Fungsi Sebaris dan Parameter Jenis yang Diselesaikan Secara Statis.

Lihat juga