연산자 오버로드(F#)

이 항목에서는 클래스나 레코드 형식에서 또는 전역 수준에서 산술 연산자를 오버로드하는 방법을 설명합니다.

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

설명

위 구문에서 operator-symbol은 +, -, *, /, = 등이 될 수 있습니다.parameter-list는 해당 연산자의 일반적인 구문에 표시되는 순서에 따라 피연산자를 지정합니다.method-body는 결과 값을 만듭니다.

연산자에 대한 연산자 오버로드는 정적이어야 합니다.+ 및 - 같은 단항 연산자에 대한 연산자 오버로드인 경우 다음 선언에서와 같이 operator-symbol에 물결표(~)를 사용하여 해당 연산자가 이항 연산자가 아니라 단항 연산자라는 것을 표시해야 합니다.

static member (~-) (v : Vector)

다음 코드에서는 연산자가 두 개뿐인 벡터 클래스를 보여 줍니다. 그중 하나는 단항 부정 연산자이고 다른 하나는 스칼라 값을 곱하기 위한 연산자입니다.이 예제의 경우 스칼라 값을 곱하는 데 두 번의 오버로드가 필요합니다. 벡터와 스칼라 값의 표시 순서에 상관없이 연산자가 작동해야 하기 때문입니다.

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

새 연산자 만들기

모든 표준 연산자를 오버로드할 수 있을 뿐만 아니라 일련의 특정 문자 시퀀스를 통해 새 연산자를 만들 수도 있습니다.사용할 수 있는 연산자 문자는!, %, &, *, +, -, ., /, <, =, >, ?, @, ^,|및 ~입니다.~ 문자에는 연산자를 단항으로 만드는 특수 의미가 있습니다. 이 문자는 연산자 문자 시퀀스의 일부가 아닙니다.이 항목의 뒷부분에 제공된 전위 연산자 및 중위 연산자에서 설명한 대로 모든 연산자가 항상 단항을 만들지는 않습니다.

정확히 어떤 문자 시퀀스를 사용하는가에 따라 연산자의 우선 순위와 결합성이 달라질 수 있습니다.결합성은 왼쪽에서 오른쪽으로 또는 오른쪽에서 왼쪽으로 설정될 수 있으며 동일한 우선 순위의 연산자가 괄호 없이 시퀀스에 나타날 때마다 결합성이 사용됩니다.

연산자 문자 .는 우선 순위에 영향을 주지 않습니다. 따라서 일반적인 곱셈과 우선 순위 및 결합성이 동일한 곱셈을 자신만의 고유한 버전으로 정의하려는 경우 .* 같은 연산자를 만들 수 있습니다.

F#의 모든 연산자에 대한 우선 순위는 기호 및 연산자 참조(F#)에 나오는 표를 참조하십시오.

오버로드된 연산자 이름

F# 컴파일러에서 연산자 식을 컴파일할 때는 해당 연산자에 대해 컴파일러를 통해 생성된 이름을 사용하는 메서드가 만들어집니다.이는 해당 메서드에 대해 MSIL(Microsoft intermediate language)에 표시되는 이름입니다. 리플렉션과 IntelliSense에도 이 이름이 표시됩니다.일반적으로 F# 코드에는 이러한 이름을 사용할 필요가 없습니다.

다음 표에는 표준 연산자와 그에 상응하는 기본 이름이 나와 있습니다.

Operator

기본 이름

[]

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

목록에 나열되어 있지 않은 다른 연산자 문자 조합을 연산자로 사용할 수도 있습니다. 여기에는 다음 표에 나와 있는 개별 문자의 이름을 결합하여 만든 이름이 사용됩니다.예를 들어 +!의 이름은op_PlusBang이 됩니다.

연산자 문자

Name

>

Greater

<

Less

+

Plus

-

Minus

*

Multiply

/

Divide

=

Equals

~

Twiddle

%

Percent

.

Dot

&

Amp

|

Bar

@

At

^

Hat

!

Bang

?

Qmark

(

LParen

,

Comma

)

RParen

[

LBrack

]

RBrack

접두사 및 중위 연산자

전위 연산자는 함수와 비슷하게 하나 이상의 피연산자 앞에 배치되는 연산자입니다.중위 연산자는 두 피연산자 사이에 배치됩니다.

특정 연산자만 전위 연산자로 사용할 수 있습니다.일부 연산자는 항상 전위 연산자이며 다른 연산자는 중위 또는 전위 연산자이고 나머지 연산자는 언제나 중위 연산자입니다.다음으로 시작 연산자 !를 제외 하 고 !=, 및 연산자 ~, 또는 반복 시퀀스를~, 항상 전위 연산자입니다.연산자 +, -, +., -., &, &&, % 및 %%는 접두사 연산자 또는 중위 연산자가 될 수 있습니다.접두사 연산자가 정의된 경우 시작 부분에 ~ 기호를 추가하여 중위 연산자 버전과 이 연산자의 접두사 버전을 구별할 수 있습니다.~는 연산자를 사용할 때 사용되지 않고, 정의할 때에만 사용됩니다.

예제

다음 코드에서는 연산자 오버로드를 사용하여 분수 형식을 구현하는 방법을 보여 줍니다.분수는 분자와 분모로 표현됩니다.분수를 약분하는 데 사용되는 가장 큰 공통 인수를 확인하는 데는 hcf 함수가 사용됩니다.

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

전역 수준 연산자

전역 수준에서 연산자를 정의할 수도 있습니다.다음은 +? 연산자를 정의하는 코드입니다.

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

위 코드의 출력은 12입니다.

이와 같은 방법으로 일반적인 산술 연산자를 재정의할 수 있습니다. F#의 범위 지정 규칙에 따르면 새로 정의한 연산자가 기본 제공 연산자보다 우선 순위가 높기 때문입니다.

키워드 inline은 일반적으로 호출 코드에 가장 잘 통합되는 작은 함수인 전역 연산자와 함께 사용되는 경우가 많습니다.연산자 함수를 인라인으로 만들면 정적으로 확인된 형식 매개 변수에 해당 함수를 사용하여 정적으로 확인된 제네릭 코드를 만들 수도 있습니다.자세한 내용은 인라인 함수(F#)정적으로 확인된 형식 매개 변수(F#)를 참조하십시오.

참고 항목

기타 리소스

멤버(F#)