2012-03-28 33 views
6

Tôi là một chút ngạc nhiên khi mã sau sẽ không biên dịch:Nhân một đúp Complex với cú đúp trong 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 

Lỗi nói

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 

Vì vậy, trong ngắn hạn

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

Định nghĩa phức tạp (*) là

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

Tại sao có thể 3 (Num a => a) và 2,5 (Phân số a => a) được so khớp mẫu với (x: + y), nhưng đôi không thể?

+2

Lưu ý rằng việc này không liên quan gì đến việc khớp mẫu. * Hợp nhất * có thể là thuật ngữ đúng. Bạn có thể nói rằng 'Num a => a' là không thể xác định với' Phức tạp đôi', nhưng 'Double' thì không. – Rotsor

+0

Bạn đang làm gì với vận tốc phức tạp? Có vẻ như một giải pháp cho một thứ thực sự là một vectơ. – leftaroundabout

+0

Phức tạp được tích hợp và đi kèm với các chức năng rất tiện dụng để có được pha, độ lớn và tất cả. Tôi không cảm thấy muốn tái phát minh ra bánh xe. Có một thư viện cho các vector 2D tôi có thể sử dụng không? – Niriel

Trả lời

15

Trước hết, các loại của người điều khiển nhân là

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

có nghĩa là bạn chỉ có thể nhân số của cùng một loại, đó là lý do nhân một Complex Double bởi một Double sẽ không hoạt động.

Vậy tại sao nhân một số phức với công thức chữ thập phân?

Nó hoạt động vì chữ số là đa hình trong Haskell, vì vậy khi bạn nhập một số nguyên như 42, nó thực sự có nghĩa là fromInteger 42. Tương tự, các chữ số thập phân như 2.3 trở thành fromRational (23 % 10). Nếu bạn kiểm tra các loại của những chức năng,

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

điều này có nghĩa rằng literals nguyên có thể là bất kỳ loại số, trong khi chữ số thập phân có thể được bất kỳ loại phân đoạn. Số phức là cả hai, đó là lý do tại sao cả hai công việc z * 3z * 2.5.

Khi bạn không xử lý văn bản, bạn phải chuyển đổi.Ví dụ, chức năng ban đầu của bạn có thể được cố định bằng cách viết:

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

Tìm chức năng chuyển đổi phù hợp rất dễ dàng sử dụng Hoogle, vì bạn có thể tìm kiếm cho các chức năng theo loại. Trong trường hợp này, tìm kiếm Double -> Complex Double cung cấp cho (:+) làm kết quả hàng đầu.

7

Bạn không thể nhân một số thực với số phức, ngay cả trong "toán thực"; khi bạn muốn lấy 2 * (2 + 3i), những gì bạn thực sự tính là (2 + 0i) * (2 + 3i). Tương tự, trong Haskell, khi bạn nói:

let z = 1 :+ 2 
z * 3 

... thì 3 cũng được chuyển đổi thành một số Complex Double, làm cho phần không tưởng tượng. Điều này chỉ xảy ra với các chữ số (2, 3.141 vv) vì chức năng chữ quá tải của Haskell; vì literals không có kiểu mặc định (chúng có thể đại diện cho giá trị của bất kỳ loại số nào), Haskell có thể nói rằng 3 có loại Complex Double trong ngữ cảnh này và các hàm chuyển đổi thích hợp được gọi tự động.

Nếu bạn muốn thực hiện chuyển đổi này theo cách thủ công, hãy tạo một số phức từ số thực là biến hoặc đã có loại cố định khác, bạn phải sử dụng hàm realToFrac, số vào bất kỳ số phân số nào (và số Complex được tính là một số phân số trong trường hợp này).

z * realToFrac (2.5 :: Double) 

Bạn cũng có thể thêm theo cách thủ công :+ 0 nếu có vẻ dễ hiểu hơn với bạn.

+4

Tôi không nghĩ đó là sự thật. 2 + 0i giống như 2! Trong "toán học thực", thực tế là một phân đoạn của các thực tế phức tạp, vì vậy nó chỉ hoạt động. Nó Haskell, bạn có thể bày tỏ mối quan hệ này, nhưng bạn cần phải rõ ràng về tiêm. Về cơ bản đó là bởi vì chúng ta không có các kiểu con trong Haskell, vì vậy chúng ta đại diện cho sự bao gồm bởi một hình thái rõ ràng. – sclv

+0

Một số phức là một số có thể được đặt dưới dạng a + bi, trong đó a và b là số thực và tôi được gọi là đơn vị ảo, trong đó i^2 = −1. Phép nhân của hai số phức được xác định bởi công thức sau: '(a + bi) (c + di) = (ac-bd) + (bc + ad) i'. Vì vậy, các số phức tạo thành một vòng mà là sản phẩm bên trong của R^2 như bạn nói; tuy nhiên, phép nhân chỉ được xác định giữa hai số phức gồm một cặp số thực, vì vậy mặc dù '2' và' 2 + 0i' bằng nhau, ta không thể tính sản phẩm của một số phức và số thực '2' mà không thay đổi định nghĩa. – dflemstr

+1

"Một số phức là một số có thể được đặt dưới dạng _a_ + _b⋅i_, trong đó _a_ và _b_ là số thực" chính xác. Và vì 0 là phần tử trung gian của phép cộng, _a_ + 0 _i_ = _a_ ∊ ℝ. Vì vậy, phép nhân giữa một phức tạp và một số thực là âm thanh toán học. Làm thế nào là nó cần thiết để thay đổi định nghĩa, nếu chúng ta chỉ có thể viết bất kỳ số thực _a_ như _a_ + 0 _i_? – leftaroundabout