2012-03-28 15 views
6

yo estaba un poco sorprendido cuando el siguiente código no se compilará:multiplicar un complejo doble con un doble en Haskell

-- Code 1 
import Complex 
type Velocity = Complex Double 
type Force = Complex Double 
type FrictionCoeff = Double 

frictionForce :: FrictionCoeff -> Velocity -> Force 
frictionForce mu vel = mu * vel 

dice El error

Couldn't match expected type `Complex Double' 
      with actual type `Double' 
Expected type: Force 
Actual type: FrictionCoeff 
In the first argument of `(*)', namely `mu' 
In the expression: mu * vel 

Así que, en resumen

-- Code 2 
let z = 1 :+ 2 
z * 3  -- Goes fine. 
z * 2.5 -- Goes fine. 
z * (2.5 :: Double) -- Explodes. 

Complex define (*) como

instance (RealFloat a) => Num (Complex a) where 
    (x:+y) * (x':+y') = (x*x'-y*y') :+ (x*y'+y*x') 

¿Por qué 3 (Num a => a) y 2.5 (Fraccional a => a) se comparan con patrones (x: + y), pero un doble no?

+2

Tenga en cuenta que esto no tiene nada que ver con la coincidencia de patrones. * Unificación * puede ser el término correcto. Puedes decir que 'Num a => a' es unificable con' Complex Double', pero 'Double' no lo es. – Rotsor

+0

¿Qué estás haciendo con velocidades complejas de todos modos? Parece una solución para algo que realmente debería ser un vector. – leftaroundabout

+0

Complejo están incorporados y vienen con funciones muy prácticas para obtener la fase, la magnitud y todo. No tengo ganas de reinventar la rueda. ¿Hay una biblioteca para vectores 2D que pueda usar? – Niriel

Respuesta

15

En primer lugar, el tipo de operador de multiplicación es

(*) :: Num a => a -> a -> a 

lo que significa que sólo se puede multiplicar números del mismo tipo, que es la razón por la multiplicación de una Complex Double por un Double no va a funcionar.

Entonces, ¿por qué la multiplicación de un número complejo con un literal decimal funciona?

Funciona porque los literales numéricos son polimórficos en Haskell, por lo que cuando escribe un entero literal como 42, realmente significa fromInteger 42. Del mismo modo, literales decimales como 2.3 se convierte en fromRational (23 % 10). Si examina los tipos de esas funciones,

fromInteger :: Num a => Integer -> a 
fromRational :: Fractional a => Rational -> a 

Esto significa que literales enteros pueden ser de cualquier tipo numérico, mientras que los literales decimales pueden ser cualquier tipo fraccional. Los números complejos son ambos, por lo que funcionan tanto z * 3 como z * 2.5.

Cuando no se trata de literales, debe convertir.Por ejemplo, su función original puede ser fijado por la escritura:

frictionForce :: FrictionCoeff -> Velocity -> Force 
frictionForce mu vel = (mu :+ 0) * vel 

Encontrar la función de conversión adecuada es muy fácil usando Hoogle, ya que se puede buscar por tipo de funciones. En este caso, la búsqueda de Double -> Complex Double da como resultado superior (:+).

7

No se puede multiplicar un número real con un número complejo, incluso en "matemática real"; cuando desee tomar 2 * (2 + 3i), lo que realmente calcule es (2 + 0i) * (2 + 3i). Del mismo modo, en Haskell, cuando dice:

let z = 1 :+ 2 
z * 3 

... luego 3 se convierte en un Complex Double así, por lo que la parte imaginaria cero. Esto solo ocurre con los números literales (2, 3.141, etc.) debido a la funcionalidad literal sobrecargada de Haskell; dado que los literales no tienen un tipo predeterminado (pueden representar valores de cualquier tipo de número), Haskell puede decir que el 3 tiene el tipo Complex Double en este contexto, y las funciones de conversión apropiadas se llaman automáticamente.

Si desea hacer esta conversión manualmente, es decir, hacer un número complejo de un número real que sea una variable o que ya tenga un tipo fijo diferente, debe usar la función realToFrac, que convierte cualquier real número en cualquier número fraccionario (y un Complex cuenta como un número fraccionario en este caso).

z * realToFrac (2.5 :: Double) 

Por supuesto, puede también añadir manualmente :+ 0 si que se ve más ordenado para usted.

+4

No creo que sea cierto. 2 + 0i es lo mismo que 2! En "matemática real", los reales son un resumen de los reales complejos, por lo que simplemente funciona. Haskell, puedes expresar esta relación, pero debes ser explícito sobre la inyección. Básicamente, eso se debe a que no tenemos subtipos en Haskell, por lo que representamos la inclusión mediante un morfismo explícito. – sclv

+0

Un número complejo es un número que se puede poner en la forma a + bi, donde a y b son números reales y se llama unidad imaginaria, donde i^2 = -1. La multiplicación de dos números complejos se define mediante la siguiente fórmula: '(a + bi) (c + di) = (ac-bd) + (bc + ad) i'. Entonces los números complejos forman un anillo que es el producto interno de R^2 como usted dice; sin embargo, la multiplicación solo se define entre dos números complejos que consisten en un par de números reales, por lo tanto, aunque '2' y' 2 + 0i' sean iguales, no se puede calcular el producto de un número complejo y el número real '2' sin cambiando la definición. – dflemstr

+1

"Un número complejo es un número que se puede poner en la forma _a_ + _b⋅i_, donde _a_ y _b_ son números reales" exactamente. Y dado que 0 es el elemento neutral de la suma, _a_ + 0 _i_ = _a_ ε ℝ. Entonces, la multiplicación entre un número complejo y uno real es matemáticamente correcto. ¿Cómo es necesario cambiar la definición, si podemos simplemente escribir cualquier número real _a_ como _a_ + 0 _i_? – leftaroundabout