Sobrecarga de operador (F#)
Este tópico descreve como sobrecarregar operadores aritméticos em um tipo de classe ou de registro, e a 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
Na sintaxe anterior, operator-symbol é um de +, -, *, /, =, e assim por diante. parameter-list especifica os operandos na ordem em que aparecem na sintaxe usual para esse operador. method-body constrói o valor resultante.
As sobrecargas de operador para operadores devem ser estáticos. As sobrecargas de operador para operadores unários, como + e -, devem usar um til (~) em operator-symbol para indicar que o operador é um operador unário e não um operador binário, conforme mostrado na seguinte declaração.
static member (~-) (v : Vector)
O código a seguir ilustra uma classe vetorial que tem apenas dois operadores, para um operador unário menos e outro para multiplicação por um único. No exemplo, duas sobrecargas para multiplicação escalar são necessárias porque o operador deve trabalhar independentemente da ordem em que o vetor e o escalar 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 todos os operadores padrão, mas você também pode criar novos operadores fora das sequências de determinados caracteres. Os caracteres de operador são permitidos !, %, &, *, +, -, ., /, <, =, >, ?, @, ^, |, e ~. O caractere de ~ tem um significado especial de fazer um operador unário, e não é parte da sequência de caracteres de operador. Nem todos os operadores podem ser feitos unários, como descrito em Operadores de prefixo e de infixo mais adiante neste tópico.
Dependendo da sequência exata de caracteres, você usa o operador terá uma determinados precedência e associatividade. Associatividade pode ser esquerda para a direita ou da direita para a esquerda e é usado sempre que os operadores de mesmo nível de precedência aparecem na ordem sem parênteses.
O caractere . de operador não afeta a precedência, para que, por exemplo, se você desejar definir sua própria versão de multiplicação que tem a mesma precedência e associatividade que multiplicação comum, você pode criar operadores como .*.
Uma tabela que mostra a precedência de todos os operadores em F# pode ser encontrada em Referência de símbolos e operadores (F#).
Nomes de operador sobrecarregados
Quando o compilador de F# compila uma expressão de operador, gerencia um método que possui um nome compilador-geradas para esse operador. Este é o nome que aparece em Microsoft intermediate language (MSIL) para o método, e também na reflexão e o IntelliSense. Você não precisa normalmente de usar esses nomes no código de F#.
A tabela a seguir mostra os operadores padrões e seus nomes gerados correspondentes.
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 do operador que não são listadas aqui podem ser usadas como operadores e ter nomes que são compostos concatenando nomes para os caracteres individuais a tabela a seguir. Por exemplo, +! se 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 |
Operadores de prefixo e de infixo
Operadoresde prefixo sejam colocados na frente de um operando ou operandos, como uma função. Operadoresde infixo sejam colocados entre os dois operandos.
Somente certos operadores podem ser usados como operadores de prefixo. Alguns operadores são sempre operadores de prefixo, outro pode ser infixo ou prefixo, e o restante é sempre operadores de infixo. Os operadores que começam com !, a não ser que !=, e o operador ~, ou repetidas sequências de~, são sempre operadores de prefixo. Os operadores +, -, +., -., &, &&, %, e %% podem ser operadores de prefixo ou operadores de infixo. Você distingue a versão do prefixo desses operadores de versão de infixo adicionando ~ no início de um operador de prefixo quando é definido. ~ não é usado quando você usa o operador, somente quando é definido.
Exemplo
O código a seguir ilustra o uso de sobrecarga de operador implementar um tipo da fração. Uma fração é representada por um numerador e um denominador. A função hcf é usada para determinar o fator comuns o maior, que é usado para reduzir 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 anterior são 12.
Você pode redefinir os operadores aritméticos normais dessa maneira porque as regras de escopo para F# determina que os operadores recentemente definidos têm precedência sobre os operadores internos.
A palavra-chave inline é freqüentemente usado com operadores globais, que são frequentemente as funções pequenas que são integradas melhor no código de chamada. Fazer funções do operador in-line também habilitar-las para trabalhar com parâmetros de tipo estaticamente resolvidos para gerar código genérico estaticamente resolvido. Para obter mais informações, consulte Funções embutidas (F#) e Parâmetros de tipo resolvidos estaticamente (F#).