Vâng, thực sự chỉ có một sai lầm cơ bản khi xử lý dữ liệu corecursive, và đó là bất cẩn bằng cách sử dụng đệ quy trên nó!
Corecursion ngụ ý rằng dữ liệu đang được tạo tăng dần theo một nghĩa nào đó. Hàm khoảng cách biểu đồ của bạn là hướng dẫn ở đây, vì có vẻ như nó nên hoạt động, vì vậy hãy nghĩ về nơi phần gia tăng phải là: Điểm bắt đầu là khoảng cách 0 từ nút đến chính nó, nếu không lớn hơn khoảng cách tối thiểu trong láng giềng ngay lập tức của riêng mình. Do đó, chúng tôi mong đợi mỗi giá trị khoảng cách sẽ tăng dần, có nghĩa là chúng tôi cần chúng phải được tự phân tích phù hợp.
Các đệ quy tại vấn đề, sau đó, đang diễn ra do sự kết hợp của (+)
và minimum
: khi tìm kiếm tối thiểu, 1
sẽ luôn luôn được ít hơn 1 + n
, mà không cần phải lo lắng về những gì n
là ... nhưng không có cách nào để so sánh Int
giây mà không tính toán toàn bộ giá trị.
Tóm lại, thuật toán hy vọng có thể so sánh số lần (1 +)
được áp dụng cho 0
chỉ khi cần thiết; đó là để nói, nó muốn số tự nhiên lười biếng được xác định bằng cách sử dụng "số không" và "người kế nhiệm".
Kìa:
data Nat = Z | S Nat
natToInt :: Nat -> Int
natToInt Z = 0
natToInt (S n) = 1 + natToInt n
instance Show Nat where show = show . natToInt
instance Eq Nat where
Z == Z = True
(S n1) == (S n2) = n1 == n2
_ == _ = False
Z /= Z = False
(S n1) /= (S n2) = n1 /= n2
_ /= _ = True
instance Ord Nat where
compare Z Z = EQ
compare Z (S _) = LT
compare (S _) Z = GT
compare (S n1) (S n2) = compare n1 n2
Và sau đó trong GHCi:
> distance 1 $ fromList [(0,[1]),(1,[0,2]),(2,[1,3]),(3,[2])]
fromList [(0,1),(1,0),(2,1),(3,2)]
Proof rằng thuật toán của bạn hoạt động [0]; việc bạn triển khai nó chỉ là không chính xác.
Bây giờ, khi sự thay đổi nhỏ, chúng ta hãy áp dụng thuật toán của bạn để một đồ thị khác nhau:
> distance 1 $ fromList [(0,[1]),(1,[0]),(2,[3]),(3,[2])]
... làm những gì chúng tôi mong đợi điều này để làm gì? Khoảng cách từ nút 1 cho các nút 2 hoặc 3 là bao nhiêu?
Chạy nó trong GHCi có kết quả rõ ràng:
fromList [(0,1),(1,0),(2,^CInterrupted.
Tuy nhiên, thuật toán hoạt động chính xác trên biểu đồ này. Bạn có thấy vấn đề không? Tại sao nó treo trong GHCi?
Nói tóm lại, bạn cần phải phân biệt rõ ràng giữa hai hình thức mà không thể được trộn lẫn một cách tự do:
- Lazy, dữ liệu có thể là vô hạn, tạo ra corecursively
- dữ liệu hữu hạn, tiêu thụ một cách đệ quy
Cả hai biểu mẫu đều có thể được chuyển đổi theo cách không cấu trúc (ví dụ: map
hoạt động trên danh sách hữu hạn và vô hạn cả hai). Codata có thể được tiêu thụ dần dần, được thúc đẩy bởi một thuật toán corecursive; dữ liệu có thể được tạo đệ quy, bị hạn chế bởi một thuật toán đệ quy.
Những gì bạn không thể làm sử dụng mã đệ quy đệ quy (ví dụ, gấp bên trái danh sách vô hạn) hoặc tạo dữ liệu corecursively (hiếm trong Haskell, do lười biếng).
[0]: Thực tế, nó sẽ không thành công trên một số yếu tố đầu vào (ví dụ: một số đồ thị bị ngắt kết nối), nhưng đó là một vấn đề khác.
Vì vậy, [Tôi đã cố gắng sử dụng một loại số nguyên lười biếng] (https://gist.github.com/1014374), nhưng tôi đã có hành vi tương tự như trước đây (treo). Bất kỳ ý tưởng những gì tôi đã làm sai? – rampion
@ rampion: Trong nháy mắt, tôi muốn nói rằng nó có thể là quá nghiêm ngặt một nơi nào đó khi kiểm tra các giá trị số nguyên lười biếng, mà có khả năng là không thể tránh khỏi. Điều này thực sự có cùng một vấn đề khái niệm khi sử dụng 'Int', nó chỉ làm cho vấn đề trở nên ít rõ ràng hơn. So sánh các giá trị cần ký hiệu và dấu cần toàn bộ phép tính do trừ. Nó vẫn có thể được, nhưng bạn phải rất cẩn thận về các hoạt động không ép buộc nhiều hơn họ thực sự cần. –