İşleç Aşırı Yüklemesi

Bu konuda, bir sınıf veya kayıt türünde ve genel düzeyde aritmetik işleçlerin aşırı yüklenmesi açıklanmaktadır.

Sözdizimi

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

Açıklamalar

Önceki söz diziminde işleç simgesi , , -, */, , =vb. simgelerden biridir+. parametre listesi işlenenleri, işleç için normal söz diziminde göründükleri sırayla belirtir. method-body, sonuçta elde edilen değeri oluşturur.

İşleçler için işleç aşırı yüklemeleri statik olmalıdır. ve -gibi birli işleçler için işleç aşırı yüklemeleri, aşağıdaki bildirimde gösterildiği gibi + işlecin ikili işleç değil bir birli işleci olduğunu belirtmek için işleç simgesinde bir tilde (~) kullanmalıdır.

static member (~-) (v : Vector)

Aşağıdaki kod, biri birli eksi, biri de skaler tarafından çarpma için olmak üzere yalnızca iki işleç içeren bir vektör sınıfını gösterir. Örnekte, skaler çarpma için iki aşırı yükleme gereklidir çünkü işlecin vektör ve skalernin görünme sırasına bakılmaksızın çalışması gerekir.

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

Yeni İşleçler Oluşturma

Tüm standart işleçleri aşırı yükleyebilirsiniz, ancak belirli karakter dizileri dışında yeni işleçler de oluşturabilirsiniz. İzin verilen işleç karakterleri : , , , , , , +, -, =.<@?^>|/ve .~*&%$! Karakterin ~ , bir işleci birli hale getirmenin özel anlamı vardır ve işleç karakter dizisinin bir parçası değildir. Tüm işleçler tekli yapılamaz.

Kullandığınız karakter dizisine bağlı olarak, işlecinizin belirli bir önceliği ve ilişkilendiriciliği olur. İlişkisellik soldan sağa veya sağdan sola olabilir ve aynı öncelik düzeyindeki işleçler parantez olmadan sırayla göründüğünde kullanılır.

İşleç karakteri . önceliği etkilemez, bu nedenle, örneğin, sıradan çarpma ile aynı önceliğe ve ilişkilendirmeye sahip kendi çarpma sürümünüzü tanımlamak istiyorsanız, gibi .*işleçler oluşturabilirsiniz.

İşleç $ tek başına ve ek simgeler olmadan durmalıdır.

F# içindeki tüm işleçlerin önceliğini gösteren bir tablo Sembol ve İşleç Başvurusu'nda bulunabilir.

Aşırı Yüklenmiş İşleç Adları

F# derleyicisi bir işleç ifadesi derlediğinde, bu işleç için derleyici tarafından oluşturulan bir ada sahip bir yöntem oluşturur. Bu, yöntemin ortak ara dilinde (CIL) ve ayrıca yansıma ve IntelliSense'te görünen addır. Normalde bu adları F# kodunda kullanmanız gerekmez.

Aşağıdaki tabloda standart işleçler ve buna karşılık gelen oluşturulan adlar gösterilmektedir.

Operatör Oluşturulan ad
[] 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

not F# içindeki işlecin sembolik bir işleç olmadığından yayılmadığını op_Inequality unutmayın. Boole ifadesini olumsuzlayan IL'i yayan bir işlevdir.

Burada listelenmeyen diğer işleç karakterleri birleşimleri işleç olarak kullanılabilir ve aşağıdaki tabloda yer alan tek tek karakterler için adları birleştirerek oluşan adlara sahiptir. Örneğin, +! olur op_PlusBang.

İşleç karakteri Veri Akışı Adı
> Greater
< Less
+ Plus
- Minus
* Multiply
/ Divide
= Equals
~ Twiddle
$ Dollar
% Percent
. Dot
& Amp
| Bar
@ At
^ Hat
! Bang
? Qmark
( LParen
, Comma
) RParen
[ LBrack
] RBrack

Ön Ek ve Infix İşleçleri

Ön ek işleçlerinin bir işleve çok benzer bir işlenenin veya işlenenin önüne yerleştirilmesi beklenir. İki işlenen arasına infix işleçlerinin yerleştirilmesi beklenir.

Ön ek işleçleri olarak yalnızca belirli işleçler kullanılabilir. Bazı işleçler her zaman ön ek işleçleridir, diğerleri infix veya ön ek olabilir ve gerisi her zaman infix işleçleridir. ile başlayan !işleçler, dışında !=, ve işleci ~veya yinelenen dizileri her zaman ön ek işleçleridir~. , , , , , , , &&, ve %%%işleçleri +ön ek işleçleri veya infix işleçleri olabilir. &-.+.- Tanımlandığında bir ön ek işlecinin başına bir ~ ekleyerek bu işleçlerin ön ek sürümünü infix sürümünden ayırt edebilirsiniz. ~ işlecini kullandığınızda, yalnızca tanımlandığında kullanılmaz.

Örnek

Aşağıdaki kod, kesir türü uygulamak için işleç aşırı yüklemesinin kullanımını gösterir. Kesir bir payda ve payda ile temsil edilir. işlevi hcf , kesirleri azaltmak için kullanılan en yüksek ortak faktörü belirlemek için kullanılır.

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

Çıkış:

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

Genel Düzeyde İşleçler

İşleçleri genel düzeyde de tanımlayabilirsiniz. Aşağıdaki kod bir işlecini +?tanımlar.

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

Yukarıdaki kodun çıktısı şeklindedir 12.

Normal aritmetik işleçleri bu şekilde yeniden tanımlayabilirsiniz çünkü F# için kapsam belirleme kuralları yeni tanımlanan işleçlerin yerleşik işleçlere göre öncelikli olduğunu belirler.

anahtar sözcüğü inline genellikle çağrı koduyla en iyi şekilde tümleştirilen küçük işlevler olan genel işleçlerle kullanılır. İşleç işlevlerinin satır içinde yapılması, statik olarak çözümlenen genel kod üretmek için statik olarak çözümlenen tür parametreleriyle çalışmalarına da olanak tanır. Daha fazla bilgi için bkz . Satır içi İşlevler ve Statik Olarak Çözümlenen Tür Parametreleri.

Ayrıca bkz.