2012-03-28 9 views
6

byłem nieco zaskoczony, gdy następujący kod nie kompilacji:Mnożąc kompleksu Pokój z podwójnym w 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 

Błąd mówi

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 

Tak więc, w krótkim

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

Kompleks definiuje (*) jako

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

Dlaczego 3 (Num a => a) i 2,5 (Ułamkowo a = a) można dopasować do wzoru (x: + y), ale Podwójne nie?

+2

Należy pamiętać, że nie ma to nic wspólnego z dopasowywaniem wzorców. * Ujednolicenie * może być właściwym terminem. Możesz powiedzieć, że 'Num a => a' jest zunifikowane z' Complex Double', ale 'Double' nie jest. – Rotsor

+0

Co robisz ze złożonymi prędkościami? Wygląda jak obejście czegoś, co naprawdę powinno być wektorem. – leftaroundabout

+0

Complex są wbudowane i mają bardzo przydatne funkcje, aby uzyskać fazę, wielkość i wszystko. Nie mam ochoty na nowo wynajdować koła. Czy istnieje biblioteka dla wektorów 2D, z których mogłabym korzystać? – Niriel

Odpowiedz

15

Po pierwsze, rodzaj mnożenia jest

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

co oznacza, że ​​można mnożyć tylko numerów tego samego typu, dlatego pomnożenie Complex Double przez Double nie będzie działać.

Dlaczego więc mnożenie liczby zespolonej za pomocą dziesiętnej literalnej pracy?

Działa, ponieważ literały liczbowe są polimorficzne w Haskell, więc kiedy wpisujesz liczbę całkowitą, taką jak 42, to naprawdę oznacza fromInteger 42. Podobnie, liczby dziesiętne, takie jak 2.3, stają się fromRational (23 % 10). Jeśli przeanalizować rodzaje tych funkcji,

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

Oznacza to, że literały całkowite mogą być dowolnego typu numerycznego, a literały dziesiętne może być dowolny typ ułamkową. Liczby zespolone są oba, dlatego też działają zarówno z * 3, jak i.

Kiedy nie masz do czynienia z literałami, musisz się nawrócić.Na przykład, pierwotna funkcja może być ustalona przez piśmie:

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

Znalezienie odpowiednia funkcja konwersji jest łatwe przy użyciu Hoogle, ponieważ można wyszukiwać według typu funkcji. W tym przypadku wyszukiwanie Double -> Complex Double daje jako wynik najwyższy (:+).

7

Nie można pomnożyć liczby rzeczywistej z liczbą zespoloną, nawet w "prawdziwej matematyce"; kiedy chcesz wziąć 2 * (2 + 3i), to, co faktycznie obliczysz, to (2 + 0i) * (2 + 3i). Podobnie w Haskell, gdy mówisz:

let z = 1 :+ 2 
z * 3 

... potem 3 zostaje przekształcony w Complex Double jak dobrze, dzięki czemu część urojoną zerowy. Dzieje się tak tylko w przypadku liczb dosłownych (2, 3.141 itd.) Z powodu przeciążonej funkcji literackiej Haskella; ponieważ literały nie mają domyślnego typu (mogą reprezentować wartości dowolnego typu liczbowego), Haskell może powiedzieć, że 3 ma typ Complex Double w tym kontekście, a odpowiednie funkcje konwersji są wywoływane automatycznie.

Jeśli chcesz wykonać tę konwersję ręcznie, tzn. Zrobić złożoną liczbę z liczby rzeczywistej, która jest zmienną lub w inny sposób ma już inny ustalony typ, musisz użyć funkcji realToFrac, która konwertuje dowolne numer na dowolną liczbę ułamkową (w tym przypadku jako numer ułamkowy liczy się Complex).

z * realToFrac (2.5 :: Double) 

Można oczywiście również ręcznie dołączyć :+ 0 jeśli to wygląda schludniej do ciebie.

+4

Nie sądzę, że to prawda. 2 + 0i jest takie samo jak 2! W "prawdziwej matematyce" reale są podporządkowaniem złożonych reali, więc po prostu działa. To Haskell, możesz wyrazić ten związek, ale musisz wyraźnie powiedzieć o wstrzyknięciu. Zasadniczo dzieje się tak dlatego, że nie mamy podtypów w Haskell, więc reprezentujemy włączenie przez wyraźny morfizm. – sclv

+0

Liczba zespolona to liczba, która może być umieszczona w postaci a + bi, gdzie aib są liczbami rzeczywistymi, a ja jest nazywana jednostką urojoną, gdzie i^2 = -1. Mnożenie dwóch liczb zespolonych definiowane jest przez następujący wzór: '(a + bi) (c + di) = (ac-bd) + (bc + ad) i'. Tak więc liczby zespolone tworzą pierścień, który jest wewnętrznym produktem R^2, jak mówisz; mnożenie jest jednak zdefiniowane tylko pomiędzy dwiema liczbami zespolonymi składającymi się z pary liczb rzeczywistych, więc nawet jeśli "2" i "2 + 0i" są równe, nie można obliczyć iloczynu liczby zespolonej i liczby rzeczywistej "2" bez zmiana definicji. – dflemstr

+1

"Liczba zespolona to liczba, którą można umieścić w formie _a_ + _b⋅i_, gdzie _a_ i _b_ są liczbami rzeczywistymi" dokładnie. A ponieważ 0 jest neutralnym elementem dodawania, _a_ + 0 _i_ = _a_ ε ℝ. Tak więc mnożenie między liczbą złożoną i rzeczywistą jest matematycznie poprawne. Jak należy zmienić definicję, jeśli możemy po prostu napisać dowolną liczbę rzeczywistą _a_ jako _a_ + 0 _i_? – leftaroundabout