Operador de sobrecarga (F#)
Este tópico descreve como sobrecarregar operadores aritméticos em uma classe ou um tipo de registro e no nível global.
// 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
Comentários
In the previous syntax, the operator-symbol is one of +, -, *, /, =, and so on.O parameter-list Especifica os operandos na ordem em que aparecem na sintaxe usual para esse operador.O method-body constrói o valor resultante.
Sobrecargas de operador para operadores devem ser estáticas.Operador sobrecargas para operadores unários, tais como + e -, deve usar um til (~) na operator-symbol para indicar que o operador é um operador unário e não um operador binário, como mostrado na seguinte declaração.
static member (~-) (v : Vector)
O código a seguir ilustra uma classe de vetor que tem apenas dois operadores, uma para negação unário e outra para multiplicação por um escalar.No exemplo, duas sobrecargas para multiplicação escalar são necessárias porque o operador deve funcionar independentemente da ordem em que o vetor e scalar aparecem.
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())
Criando novos operadores
Você pode sobrecarregar os operadores padrão, mas você também pode criar novos operadores de seqüências de determinados caracteres.Allowed operator characters are !, %, &, *, +, -, ., /, <, =, >, ?, @, ^,|, and ~.O ~ tem um significado especial de fazer um unário de operador de caracteres e não é parte da seqüência de caracteres do operador.Nem todos os operadores podem ser feitos unários, conforme é descrito em prefixo e operadores de Infix posteriormente neste tópico.
A seqüência de caracteres exata que você use, dependendo da sua operadora terá determinados precedência e associatividade.Associatividade tanto pode ser deixada para a direita ou da direita para a esquerda e é usada sempre que operadores do mesmo nível de prioridade aparecem na seqüência sem parênteses.
O caractere de operador . não afeta a precedência, para que, por exemplo, se você quiser definir sua própria versão de multiplicação tem a mesma precedência e associatividade como multiplicação comum, você poderia criar operadores, como .*.
Uma tabela que mostra a precedência de operadores de todos os F# pode ser encontrada no Símbolo e o referência de operador (F#).
Nomes de operador sobrecarregado
Quando o compilador F# compila uma expressão do operador, ele gera um método que tem um nome gerado pelo compilador para esse operador.Este é o nome que aparece na Microsoft intermediate language (MSIL) para o método e também em reflexão e IntelliSense.Normalmente, não é necessário usar esses nomes no código do F#.
A tabela a seguir mostra os operadores padrão e seus correspondentes nomes gerados.
Operador |
Nome gerado |
---|---|
[] |
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 |
Outras combinações de caracteres de operador que não estão listados aqui podem ser usadas como operadores e têm nomes que são compostos pela concatenação de nomes para os caracteres individuais da tabela a seguir.Por exemplo, +!becomes op_PlusBang.
Caractere de operador |
Nome |
---|---|
> |
Greater |
< |
Less |
+ |
Plus |
- |
Minus |
* |
Multiply |
/ |
Divide |
= |
Equals |
~ |
Twiddle |
% |
Percent |
. |
Dot |
& |
Amp |
| |
Bar |
@ |
At |
^ |
Hat |
! |
Bang |
? |
Qmark |
( |
LParen |
, |
Comma |
) |
RParen |
[ |
LBrack |
] |
RBrack |
Prefixo e nos operadores fixos
Prefixo operadores devem ser colocados na frente de um operando ou operandos, muito semelhante a uma função.Infix operadores devem ser colocados entre os dois operandos.
Apenas determinados operadores podem ser usados como operadores de prefixo.Alguns operadores são sempre operadores de prefixo, outras pessoas podem ser fixos ou o prefixo e o resto sempre são infix operadores.Os operadores que começam com !, exceto !=e o operador de ~, ou repetida seqüências de~, sempre são operadores de prefixo.The operators +, -, +., -., &, &&, %, and %% can be prefix operators or infix operators.Distinguir a versão de prefixo desses operadores da versão fixos, adicionando um ~ no início de um operador de prefixo quando ele estiver definido.O ~ não é usado quando você usa o operador, somente quando ele estiver definido.
Exemplo
O código a seguir ilustra o uso de sobrecarga de operador para implementar um tipo de fração.Uma fração é representada por um numerador e um denominador.A função hcf é usada para determinar o fator comum máxima, que é usado para reduzir as frações.
// 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())
Operadores no nível Global
Você também pode definir operadores no nível global.O código a seguir define um operador +?.
let inline (+?) (x: int) (y: int) = x + 2*y
printf "%d" (10 +? 1)
A saída do código acima é 12.
Você pode redefinir os operadores aritméticos regulares dessa maneira porque as regras de escopo para F# determinam que os operadores recém-definido prevalecem sobre os operadores internos.
A palavra-chave inline é geralmente usado com operadores globais, que geralmente são pequenas funções melhor integrados ao código de chamada.Inline de funções de operador de tomada também permite trabalhar com parâmetros de tipo resolvido estaticamente para produzir código genérico estaticamente resolvido.Para obter mais informações, consulte Funções embutidas (F#) e Resolvido estaticamente os parâmetros de tipo (F#).