演算子のオーバーロードOperator Overloading

このトピックでは、クラス型またはレコード型の算術演算子をオーバーロードする方法と、グローバル レベルで算術演算子をオーバーロードする方法について説明します。This topic describes how to overload arithmetic operators in a class or record type, and at the global level.

構文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

RemarksRemarks

前の構文で、演算子記号の 1 つ+-*/=など。In the previous syntax, the operator-symbol is one of +, -, *, /, =, and so on. パラメーター リストそのオペレーターの通常の構文で表示される順序でオペランドを指定します。The parameter-list specifies the operands in the order they appear in the usual syntax for that operator. メソッド本体結果の値を構築します。The method-body constructs the resulting value.

演算子のオーバーロードには、static を指定する必要があります。Operator overloads for operators must be static. 演算子がなどの単項演算子のオーバー ロード+-、チルダを使用する必要があります (~) で、演算子記号いることを示す演算子は、単項演算子および二項演算子ではないのように、次の宣言。Operator overloads for unary operators, such as + and -, must use a tilde (~) in the operator-symbol to indicate that the operator is a unary operator and not a binary operator, as shown in the following declaration.

static member (~-) (v : Vector)

次のコードは、演算子を 2 つだけ含むベクター クラスを示しています。その 1 つは単項マイナス演算子で、もう 1 つはスカラーによる乗算演算子です。The following code illustrates a vector class that has just two operators, one for unary minus and one for multiplication by a scalar. この例では、ベクターとスカラーの記述順序とは無関係に演算子が機能する必要があるため、必要となるスカラー乗算のオーバーロードは 2 つです。In the example, two overloads for scalar multiplication are needed because the operator must work regardless of the order in which the vector and scalar appear.

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

新しい演算子の作成Creating New Operators

標準演算子はすべてオーバーロードすることができますが、特定の文字を並べて新しい演算子を作成することもできます。You can overload all the standard operators, but you can also create new operators out of sequences of certain characters. 使用できる演算子文字は!%&*+-./<=>?@^|、および~します。Allowed operator characters are !, %, &, *, +, -, ., /, <, =, >, ?, @, ^, |, and ~. ~ には、演算子を単項演算子にするという特別な意味があるため、演算子の文字シーケンスに含めることはできません。The ~ character has the special meaning of making an operator unary, and is not part of the operator character sequence. すべての演算子には、単項を作成できます。Not all operators can be made unary.

作成した演算子は、使用した厳密な文字シーケンスに従って、特定の優先順位と結合規則を持つようになります。Depending on the exact character sequence you use, your operator will have a certain precedence and associativity. 結合規則を使用する方向は、左から右にすることも、右から左にすることもできます。シーケンスの中で同じレベルの優先順位を持つ演算子を、かっこを使用せずに記述する場合は、必ず結合規則を使用します。Associativity can be either left to right or right to left and is used whenever operators of the same level of precedence appear in sequence without parentheses.

演算子文字の . は、優先順位に影響しません。そのため、通常の乗算演算子と同じ優先順位と結合規則を持つ独自の形式の乗算演算子を定義する場合などに、.* のような演算子を作成できます。The operator character . does not affect precedence, so that, for example, if you want to define your own version of multiplication that has the same precedence and associativity as ordinary multiplication, you could create operators such as .*.

演算子のみ??<-で始めることは?します。Only the operators ? and ?<- may start with ?.

すべての演算子の優先順位を示すテーブルF#で見つかるシンボルと演算子のリファレンスします。A table that shows the precedence of all operators in F# can be found in Symbol and Operator Reference.

オーバーロードされた演算子の名前Overloaded Operator Names

F# コンパイラが演算子の式をコンパイルするときに、その演算子のメソッドが生成されます。このメソッドには、コンパイラにより生成された名前が付けられます。When the F# compiler compiles an operator expression, it generates a method that has a compiler-generated name for that operator. これは、このメソッドの Microsoft Intermediate Language (MSIL) で使用される名前です。また、リフレクションと IntelliSense でも使用されます。This is the name that appears in the Microsoft intermediate language (MSIL) for the method, and also in reflection and IntelliSense. 通常、この名前を F# コードで使用する必要はありません。You do not normally need to use these names in F# code.

次の表に、標準演算子と生成される名前を示します。The following table shows the standard operators and their corresponding generated names.

演算子Operator 生成された名前Generated name
[] 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

ここに記載されていないその他の演算子文字の組み合わせも演算子として使用できます。その場合の名前は、次の表の各文字の名前を連結して生成されます。Other combinations of operator characters that are not listed here can be used as operators and have names that are made up by concatenating names for the individual characters from the following table. たとえば、+!For example, +! なりますop_PlusBangします。becomes op_PlusBang.

演算子文字Operator character 名前Name
> Greater
< Less
+ Plus
- Minus
* Multiply
/ Divide
= Equals
~ Twiddle
% Percent
. Dot
& Amp
| Bar
@ At
^ Hat
! Bang
? Qmark
( LParen
, Comma
) RParen
[ LBrack
] RBrack

前置演算子と挿入演算子Prefix and Infix Operators

プレフィックス演算子がオペランドの場合、関数と同様に、または複数のオペランドの前に配置するが必要です。Prefix operators are expected to be placed in front of an operand or operands, much like a function. "挿入辞" 演算子が 2 つのオペランドの間で配置する必要があります。Infix operators are expected to be placed between the two operands.

前置演算子として使用できるのは特定の演算子だけです。Only certain operators can be used as prefix operators. 演算子は、常に前置演算子であるもの、挿入演算子にも前置演算子にもなるもの、および常に挿入演算子であるものに分けられます。Some operators are always prefix operators, others can be infix or prefix, and the rest are always infix operators. ! で始まる演算子 (!= を除く) と ~ 演算子、または ~ の繰り返しシーケンスは、常に前置演算子です。Operators that begin with !, except !=, and the operator ~, or repeated sequences of~, are always prefix operators. 演算子 +-+.-.&&&%、および %% は、前置演算子にも挿入演算子にもなります。The operators +, -, +., -., &, &&, %, and %% can be prefix operators or infix operators. これらの演算子の前置バージョンを挿入バージョンと区別するには、定義時に前置演算子の先頭に ~ を追加します。You distinguish the prefix version of these operators from the infix version by adding a ~ at the beginning of a prefix operator when it is defined. ~ は、演算子の使用時には使用されず、定義時にのみ使用されます。The ~ is not used when you use the operator, only when it is defined.

Example

次のコードは、分数型を実装する際の演算子のオーバーロードの使用例です。The following code illustrates the use of operator overloading to implement a fraction type. 分数は、分子と分母で表されます。A fraction is represented by a numerator and a denominator. hcf 関数を使用して最大公約数を求め、その公約数を使用して約分します。The function hcf is used to determine the highest common factor, which is used to reduce fractions.

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

グローバル レベルの演算子Operators at the Global Level

演算子は、グローバル レベルで定義することもできます。You can also define operators at the global level. 次のコード定義の演算子+?します。The following code defines an operator +?.

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

上記のコードの出力は 12 になります。The output of the above code is 12.

F# のスコープ規則では、新しく定義された演算子が組み込み演算子よりも優先されるため、このようにして、通常の算術演算子を再定義できます。You can redefine the regular arithmetic operators in this manner because the scoping rules for F# dictate that newly defined operators take precedence over the built-in operators.

キーワード inline は、グローバル演算子と共に使用されることがよくあります。グローバル演算子は、通常、呼び出し元のコードに適宜組み込まれる小規模関数にします。The keyword inline is often used with global operators, which are often small functions that are best integrated into the calling code. 演算子関数をインライン化することにより、その関数を静的に解決された型パラメーターと共に使用して、静的に解決されたジェネリック コードを作成することもできます。Making operator functions inline also enables them to work with statically resolved type parameters to produce statically resolved generic code. 詳細については、次を参照してください。インライン関数Statically Resolved Type Parametersします。For more information, see Inline Functions and Statically Resolved Type Parameters.

関連項目See also