Có thể giúp bạn lùi lại từ khái niệm Monad một chút khi suy nghĩ về mã này, nếu bạn thấy rằng các thao tác danh sách cũ đơn giản hơn là tự nhiên đối với bạn. Vì vậy, bạn có thể viết lại mã ví dụ (với một chút dọn dẹp cho mức độ dễ đọc) như:
type KnightPos = (Int,Int)
moveKnight :: KnightPos -> [KnightPos]
moveKnight (c,r) = filter legal candidates where
candidates = [(c+2,r-1), (c+2,r+1), (c-2,r-1), (c-2,r+1),
(c+1,r-2), (c+1,r+2), (c-1,r-2), (c-1,r+2)]
legal (c',r') = c' `elem` [1..8] && r' `elem` [1..8]
in3 :: KnightPos -> [KnightPos]
in3 start = concatMap moveKnight (concatMap moveKnight (moveKnight start))
canReachIn3 :: KnightPos -> KnightPos -> Bool
canReachIn3 start end = end `elem` in3 start
Nước sốt bí mật là concatMap
. Như bạn có thể đã biết, nó đồng nghĩa với >>=
trong đơn List
, nhưng bây giờ có thể hữu ích hơn khi nghĩ về nó như ánh xạ hàm số KnightPos -> [KnightPos]
trên danh sách [KnightPos]
để tạo danh sách các danh sách [[KnightPos]]
và sau đó làm phẳng kết quả trở lại vào một danh sách duy nhất. Được rồi, vì vậy bây giờ chúng tôi đã phân phối với monads cho thời điểm này, chúng ta hãy nhìn lại câu đố ... Giả sử KnightPos
ban đầu của bạn là (4,4)
và bạn muốn theo dõi tất cả các chuỗi di chuyển có thể có từ vị trí đó.Vì vậy, xác định một loại từ đồng nghĩa:
type Sequence = [KnightPos]
Sau đó, bạn muốn moveKnight
để hoạt động trên những trình tự này, việc tìm kiếm tất cả các di chuyển có thể từ vị trí cuối cùng trong dãy:
moveKnight :: Sequence -> [Sequence]
Vì vậy, bắt đầu từ một chuỗi [(4,4)]
, chúng tôi sẽ có danh sách các chuỗi: [[(4,4), (6,3)], [(4,4), (6,5)], [(4,4), (2,3)], ... ]
. Sau đó, tôi nghĩ rằng sự thay đổi duy nhất mà bạn sẽ cần phải thực hiện để in3
là để sửa chữa chữ ký kiểu của nó cho phù hợp:
in3 :: Sequence -> [Sequence]
Tôi không nghĩ rằng những thay đổi thực hiện thực tế. Cuối cùng, bạn sẽ muốn canReachIn3
để tìm một cái gì đó như:
canReachIn3 :: KnightPos -> KnightPos -> [Sequence]
Tôi đi các chi tiết thực hiện ra ở đây vào mục đích vì tôi không muốn làm hỏng các câu đố cho bạn hoàn toàn, nhưng tôi hy vọng tôi đã minh họa quan điểm ở đây rằng không có bất cứ điều gì đặc biệt "đặc biệt" về một danh sách, hoặc một danh sách các danh sách, hoặc bất cứ điều gì. Tất cả những gì chúng ta đã thực hiện được thay thế một hàm của kiểu KnightPos -> [KnightPos]
với một hàm mới của kiểu Sequence -> [Sequence]
- khá nhiều hình dạng giống nhau. Vì vậy, điền vào việc thực hiện của từng chức năng sử dụng bất kỳ phong cách cảm thấy tự nhiên, và sau đó một khi bạn có nó làm việc, quay trở lại phong cách monadic, thay thế concatMap
với >>=
và như vậy.
Cảm ơn bạn đã cho tôi đủ để tôi tiếp tục tồn tại. –