2010-02-09 16 views
18

¿F # tiene el mismo problema que C# donde no se pueden usar directamente operadores aritméticos con tipos T genéricos?¿F # tiene soporte aritmético genérico?

¿Se puede escribir una función Suma genérica que devuelva la suma de cualquier valor que admita la suma aritmética?

Respuesta

4

F # tiene un soporte limitado para esto. Una buena solución general probablemente involucre clases de tipo, que no son compatibles con el CLR en general, o F # en particular.

F # tiene operadores aritméticos sobrecargados que utilizan las funciones 'restricciones de miembros estáticos' y 'en línea'. Esta es la magia que permite, p. el operador + para trabajar tanto en int sy float s. Puede escribir las funciones inline cuyas implementaciones se basan en los operadores matemáticos incorporados y hacer algún progreso, pero no es trivial hacerlo en general. Puede consultar, p. el código fuente a Array.sum (en array.fs en FSharp.Core) en la distribución fuente F # que viene junto con el CTP para obtener una idea.

Ver también las 'restricciones miembro estáticas' y 'simular clases de tipo' parte de esta respuesta:

Functions with generic parameter types

, así como varios bits de la biblioteca como

http://msdn.microsoft.com/en-us/library/ee370581(VS.100).aspx

http://msdn.microsoft.com/en-us/library/ee340262(VS.100).aspx

2

El mejor mecanismo que conozco para realizar La aritmética genérica es clases de tipos, que lamentablemente ni C#, F #, ni el tiempo de ejecución .Net son compatibles en general. Sin embargo, se les puede simular a sí mismo con la mano, como se menciona en esta entrada del blog:

Type Classes Are The Secret Sauce

Esta técnica debería funcionar en C# 2.0 o posterior (usando el anonimato delegados/lambdas).

A menudo, las personas recurren a las interfaces, pero correr en un par de problemas

  1. no se puede declarar que el tipo existente implementar una interfaz, por lo que no se puede definir instancia de esa interfaz para construida en tipos como int .
  2. Las interfaces no pueden restringir el tipo de otros argumentos a los métodos.

Una interfaz declara que, para todas las implementaciones, todos los métodos en esa interfaz toman el mismo tipo de parámetro "este" implícito. Si Foo implementa alguna interfaz, entonces obviamente el parámetro 'this' debe ser de tipo Foo para esa implementación. Pero no hay forma de exigir que los otros parámetros del método también sean del tipo Foo.

Type classes le permiten (entre otras cosas) realizar este tipo de restricción en todos los parámetros del método, no solo en el primer parámetro.

Como se menciona en el artículo citado anteriormente, puede simular clases de tipos pasando tablas de funciones como argumentos explícitos.

(Comunidad wiki: registraría un ejemplo de ese artículo traducido en C# aquí, pero se acabó el tiempo con explicación de largo aliento)

4

que podría hacer algo como esto.

let inline sum<'a when 'a : (static member (+) : 'a -> 'a -> 'a)> a b = 
    a + b 

let result = sum<int> 3 4 

Sin embargo si intento let result = sum 3 4 me sale el error "type ambiguity inherent in the use of the operator '(+)'"

+2

Pensé que necesita usar '^ a' en lugar de''a' cuando usa restricciones estáticas en F #, pero puedo estar equivocado ... –

+0

Ambos funcionan en este caso. – gradbot

+5

En este caso particular, ni siquiera necesita especificar los parámetros genéricos o restricciones porque se pueden inferir: 'let inline sum a b = a + b' – kvb

8

Como se mencionó Brian, hay una cierta incorporado en el apoyo a Aritmética genérico y se puede utilizar 'restricciones estáticas' que le permiten definir algunos genéricos funciona usted mismo (aunque esto es un poco limitado).

Además de esto, también puede usar 'asociaciones numéricas' dinámicas, que es un poco más lenta cuando se usa en una función, pero se puede usar muy bien, por ejemplo, para definir su propio vector o tipo de matriz. Aquí hay un ejemplo:

#r "FSharp.PowerPack.dll" 
open Microsoft.FSharp.Math 

let twoTimesLarger (n:'a) (m:'a) = 
    let ops = GlobalAssociations.GetNumericAssociation<'a>() 
    let sel = if ops.Compare(n, m) > 0 then n else m 
    ops.Multiply(sel, ops.Add(ops.One, ops.One)) 

Primero tenemos que hacer referencia a la biblioteca F # PowerPack que contiene la funcionalidad. Luego definimos una función genérica con una firma 'a -> 'a -> 'a. La primera línea obtiene dinámicamente operaciones numéricas para trabajar con el tipo 'a (básicamente utiliza alguna tabla de búsqueda con el tipo como clave). Luego puede usar los métodos del objeto de operaciones numéricas para realizar cosas como multiplicación, adición (Multiply, Add) y muchas otras. La función trabaja con los números:

twoTimesLarger 3 4 
twoTimesLarger 2.3 2.4 
twoTimesLarger "a" "b" // Throws an exception! 

Al definir su propio tipo numérico, puede definir sus operaciones numéricas y registrarlos utilizando GlobalAssociations.RegisterNumericAssociation. Creo que esto también significa que podrá usar F # Matrix<YourType> y Vector<YourType> incorporados después de registrar las operaciones.

Cuestiones relacionadas