2012-09-05 28 views
8

Tôi đã mã Haskell sau:không thể phù hợp dự kiến ​​loại 'Int 'với kiểu thực tế 'Integer'

-- Problem 69 

import ProjectEuler 

phi :: Integer -> Integer 
phi n = n * product [p - 1 | p <- primeDivisors n] `div` product [p | p <- primeDivisors n] 
-- primeDivisors n is a list of the prime divisors of n 

maxRatio :: (Int, Int, Double) -> (Int, Int, Double) -> (Int, Int, Double) 
maxRatio [email protected](_, _, x) [email protected](_, _, y) 
    | x > y = t1 
    | otherwise = t2 

main = print (foldl 
       maxRatio 
       (0, 0, 0.0) 
       [(n, phi n, ratio) | n <- [2..max], let ratio = fromIntegral n/(fromIntegral (phi n))] 
      ) 
    where max = 1000 

mang đến cho các lỗi sau:

Couldn't match expected type `Int' with actual type `Integer' 
In the expression: n 
In the expression: (n, phi n, ratio) 
In the third argument of `foldl', namely 
    `[(n, phi n, ratio) | 
     n <- [2 .. max], 
     let ratio = fromIntegral n/(fromIntegral (phi n))]' 

tôi nghi ngờ rằng trong ba (0, 0, 0.0) số 0 là loại Int. Có phải 0 luôn nhập Int hoặc là ghci khấu trừ loại là Int trong trường hợp này? Nếu sau này, làm thế nào để buộc nó phải nhập Integer thay thế? Hoặc có cái gì khác gây ra lỗi này?

Trả lời

15

Haskell thường có thể suy ra các loại chữ số như 0 như bất cứ điều gì phù hợp loại bạn cần họ được. Điều này là bởi vì nó biết những gì các chức năng bạn vượt qua chúng; nếu tôi có chức năng phi :: Integer -> Integer và tôi gọi phi 0, Haskell biết rằng 0 cụ thể đó phải là Integer. Cũng tốt nếu tôi gọi hàm pho :: Int -> Int với pho 0; rằng cụ thể 0 được phỏng đoán là Int.

Tuy nhiên IntInteger là các loại khác nhau và không có cách nào một trong những 0 cụ thể có thể được chuyển đến cả hai phipho.

Vấn đề của bạn đơn giản là các bộ tóan mà maxRatio giao dịch được nhập (bởi bạn) (Int, Int, Double), nhưng một bộ dữ liệu đó được tạo thành (n, phi n, ratio). Vì số phi mất và trả về Integer, số n trong biểu thức đó phải là Integer. Nhưng sau đó điều đó không làm việc cho maxRatio, vì vậy bạn nhận được lỗi.

Tùy thuộc vào loại hình bạn thực sự muốn (Int hoặc Integer), tất cả các bạn cần làm là thay đổi kiểu chữ ký của phi hoặc maxRatio để họ đang làm việc với cùng một loại số. Haskell sẽ quyết định rằng bạn viết theo nghĩa đen là 0 s là bất kỳ loại số nào cần thiết để thực hiện công việc đó, được cung cấp có một là có thể làm cho nó hoạt động!

Lưu ý rằng lỗi nhắn tin cho đặc biệt nói với bạn rằng đó là n trong (n, phi n, ratio) đã được dự kiến ​​sẽ là một Int và thực sự là một Integer. (0, 0, 0.0) tuple không bao giờ được đề cập.Thông thường các lỗi loại xuất phát ở đâu đó khác với nơi trình biên dịch trỏ bạn (vì tất cả trình biên dịch có thể làm là tại chỗ các chuỗi suy luận khác nhau tạo ra các yêu cầu không phù hợp về loại thứ gì đó, không có cách nào để biết phần nào của toàn bộ quá trình là "sai"), nhưng trong trường hợp này nó đã làm khá tốt.

Haskell nhận được một đại diện xấu (khá hợp lý) cho các thông báo lỗi không thể sửa được, nhưng nó có thể giúp bạn bắt đầu từ những gì trình biên dịch nói với bạn. từ mã của bạn. Điều này sẽ gây đau đầu tiên, nhưng bạn sẽ nhanh chóng phát triển khả năng đọc viết cơ bản trong các thông báo lỗi của Haskell (ít nhất là những thông điệp đơn giản hơn) sẽ giúp bạn phát hiện ra các loại lỗi này một cách nhanh chóng. hệ thống cho bạn.

+0

Cảm ơn lời giải thích rõ ràng. Tôi nghĩ rằng '0' là đa hình trong loại của nó, nhưng đôi mắt noob của tôi không thể thấy bất kỳ lý do nào khác cho suy luận kiểu đã đưa ra lỗi. Tất nhiên, tôi chỉ đơn giản là bỏ lỡ khai báo kiểu khai báo của tôi cho 'maxRatio'. –

4

n đang được suy ra là Int do loại maxRatio, trong khi loại phi cho biết cần là Integer. Cách khắc phục đơn giản nhất là thay đổi loại maxRatio để sử dụng Integer hoặc thậm chí chỉ a vì nó không chạm vào các giá trị đó.

+0

Doh! Tôi đã bỏ lỡ chi tiết đó. Tôi đang xem xét lại một số mã mà tôi đã viết một thời gian trước có lẽ trước khi tôi học cách sử dụng Integer gần như độc quyền. –

3

Nó đang được suy ra, vì vậy bạn chỉ có thể thay đổi chữ ký loại maxRatio. Tuy nhiên, nếu bạn đã bao giờ cần phải thay đổi một rõ ràng Int một Integer, sử dụng toInteger :: (Integral a) => a -> Integer

+0

Tôi thường sử dụng 'fromIntegeral', đặc biệt là khi tôi đang sử dụng' length'. Có sự khác biệt nào giữa việc sử dụng 'fromIntegral' thay vì' toInteger'? –

+0

@ Code-Guru: Tôi nghĩ sự khác biệt duy nhất là 'fromIntegral' có thể trả về bất kỳ kiểu' Num' nào, vì vậy nó ít rõ ràng hơn khi đọc nó kiểu suy luận như thế nào. – amindfv

+1

@ Code-Guru: thực ra, 'fromIntegral' được định nghĩa là' fromIntegral = fromInteger. toInteger' - vì vậy nó chắc chắn chỉ là một sự cân bằng giữa tính linh hoạt và khả năng đọc – amindfv

1

Chữ ký loại của bạn không nhất quán - thay thế Int bằng Integer trong toàn bộ.

Các vấn đề liên quan