2013-06-03 26 views
5

Hãy chơi trò chơi. Có hai đống chúng tôi sẽ sử dụng, cả hai đều bao gồm các chip màu đen/trắng.Cập nhật nhiều trường con của trường bằng cách sử dụng Ống kính ekmett

data Pile = Pile { _blacks, _whites :: Int } 
makeLenses ''Pile 

data Game = Game { _pileA, _pileB :: Pile } 
makeLenses ''Game 

Một động thái thực sự thông minh sẽ là chuyển một con chip màu đen trong cọc A và một con chip trắng - thành đống B. Nhưng làm cách nào?

cleverMove :: Game -> Game 
cleverMove game = game & pileA . blacks -~ 1 
         & pileA . whites +~ 1 
         & pileB . blacks +~ 1 
         & pileB . whites -~ 1 

Không phải rất thanh lịch. Làm thế nào tôi có thể làm điều đó mà không cần tham khảo mỗi đống hai lần?

Điều duy nhất tôi đã đưa ra (và tôi không thích nó):

cleverMove game = game & pileA %~ (blacks -~ 1) 
           . (whites +~ 1) 
         & pileB %~ (blacks +~ 1) 
           . (whites -~ 1) 

(Xin lỗi trước nếu nó rõ ràng - Tôi kinda mới để ống kính và tôi cảm thấy mất trên biển của combinators và khai thác lens cung cấp. có lẽ tất cả mọi thứ cho nhu cầu của mọi người trốn ở đó. Không phải là nó xấu, tất nhiên! nhưng tôi muốn có cũng bao gồm một hướng dẫn đầy đủ.)

+2

Bạn không thích điều gì về tùy chọn 2? Nó không thể súc tích hơn thế, đúng không? – leftaroundabout

+0

@leftaroundabout Tôi không thích rằng tôi đã phải sử dụng dấu ngoặc đơn, trở nên vụng về khi có nhiều biểu thức liên quan đến nhau - chẳng hạn như 'do' khối và các cấp độ lồng nhau khác. – Artyom

+4

Tôi nghĩ rằng nó sẽ giúp ích nếu bạn cho thấy một số mã giả thô tương ứng với cú pháp lý tưởng của bạn. –

Trả lời

5

một Traversal là một sự tổng quát của Lens mà "trọng tâm "trên nhiều giá trị. Hãy nghĩ về nó như traverse cho phép bạn bước qua một giá trị sửa đổi Traversable t trong một số Applicative (traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b) trông khá giống loại Lens, bạn sẽ nhận thấy — chỉ cần nghĩ đến t b ~ whole).

Đối với một số Traversal, chúng tôi chỉ có thể chọn các giá trị mà chúng tôi muốn thay đổi. Ví dụ: hãy để tôi khái quát hóa Pile của bạn một chút và xây dựng một Traversal.

data Pile = Pile { _blacks :: Int, _whites :: Int, _name :: String } deriving (Show) 
$(makeLenses ''Pile) 

counts :: Traversal' Pile Int 
counts f (Pile blacks whites name) = 
    Pile <$> f blacks <*> f whites <*> pure name 

như vậy là bạn có thể thấy, tôi sẽ ghé thăm cả blackswhites với f nhưng để lại namepure. Đây gần giống như cách bạn viết một ví dụ Traversable ngoại trừ bạn luôn truy cập tất cả của các phần tử (đồng nhất) có trong cấu trúc Traversable.

Main*> Pile 0 0 "test" & counts +~ 1 
Pile {_blacks = 1, _whites = 1, _name = "test"} 

này là không đủ để làm những gì bạn muốn, tuy nhiên, kể từ khi bạn cần phải cập nhật các lĩnh vực của bạn trong cách khác nhau. Đối với điều đó, bạn cần phải xác định logic của bạn và đảm bảo nó duy trì một bộ quy tắc hoàn toàn khác.

blackToWhite :: Pile -> Pile 
blackToWhite = (blacks -~ 1) . (whites +~ 1) 
Các vấn đề liên quan