2013-06-13 20 views
8

Tôi có một danh sách các bản ghi và cần chức năng tìm kiếm danh sách bản ghi với tên đã cho và sửa đổi giá trị của hồ sơ này HOẶC nếu không có bản ghi khớp nối thêm một bản ghi mới vào danh sách kết quả. Đây là mã của tôi cho đến nay:Thay đổi phần tử của danh sách nếu nó giữ lại một số điều kiện hoặc thêm một phần mới nếu không, sử dụng Data.Lens

import Control.Lens 
import Control.Applicative ((<$>), pure) 
import Data.List (any) 

data SomeRec = SomeRec { _name :: String, _val :: Int } 
$(makeLenses ''SomeRec) 

_find :: (a -> Bool) -> Simple Traversal [a] a 
_find _ _ [] = pure [] 
_find pred f (a:as) = if pred a 
         then (: as) <$> f a 
         else (a:) <$> (_find pred f as) 

changeOrCreate :: [SomeRec] -> String -> (Int -> Int) -> [SomeRec] 
changeOrCreate recs nameToSearch valModifier = 
    if (any (\r -> r^.name == nameToSearch) recs) 
    then over (_find (\r -> r^.name == nameToSearch)) (over val valModifier) recs 
    else recs ++ [SomeRec nameToSearch (valModifier 0)] 

Nó hoạt động tốt, nhưng tôi đang tự hỏi nếu có một cách trực tiếp hơn của văn bản này sử dụng Data.Lens (không có if -construct)? Ngoài ra, tôi có phải viết hàm _find hoặc có điều gì đó tương đương trong thư viện không?

Cập nhật: Đây là một Gist của nguồn để thử nghiệm: https://gist.github.com/SKoschnicke/5795863

Trả lời

2

Làm thế nào về:

changeOrCreate :: String -> (Int -> Int) -> [SomeRec] -> [SomeRec] 
changeOrCreate nameToSearch valModifier = 
    pos . val %~ valModifier 
    & outside (filtered (not . has pos)) %~ (. newRec) 
    where 
    pos :: Traversal' [SomeRec] SomeRec 
    pos = taking 1 (traversed . filtered (anyOf name (== nameToSearch))) 
    newRec = (SomeRec nameToSearch 0 :) 
+0

Vì vậy, một 'Prism' cho phép tôi thay đổi danh sách? Tôi nghĩ rằng tôi phải kiểm tra để hiểu hoàn toàn, sẽ làm điều đó vào ngày mai! –

+0

Hoạt động độc đáo! Nhưng theo như tôi hiểu, với việc sử dụng 'lọc',' pos' không còn là 'Traversal' hợp lệ nữa, đúng không? Do đó, nó thường không tương thích với các traversals khác. –

+0

Đó thực sự không phải là 'Traversal' hợp lệ nếu bạn sử dụng nó để thay đổi trường' name' và do đó làm cho vị từ không giữ nữa ("quy tắc" có thể bị vỡ, như được minh họa trong câu trả lời của Matvey), nhưng ngược lại, nó có thể phối hợp với traversals. – yairchu

2

Tôi không biết, nhưng bạn có thể viết một số như

changeOrCreate [] n f = [SomeRec n (f 0)] 
changeOrCreate (r:rs) n f | r^.name == n = (over val f) r:rs 
          | otherwise = r: changeOrCreate rs n f 
+0

Đó được thoát khỏi sự lặp lại, cảm ơn bạn! –

2

Vì vậy, _find không phải là thực sự là một Traversal :

> [1..10] & over (_find odd) succ . over (_find odd) succ 
[2,2,4,4,5,6,7,8,9,10] 
> [1..10] & over (_find odd) (succ . succ) 
[3,2,3,4,5,6,7,8,9,10] 

Điều đó cũng giống như vậy filtered không phải là trình duyệt al.

Bắt một phần có thể được bắt chước với filtered (đó là ổn ở đây từ Fold không có bất kỳ pháp luật):

> [1..10] ^? _find even 
Just 2 
> [1..10] ^? _find (> 20) 
Nothing 
> [1..10] ^? folded . filtered even 
Just 2 
> [1..10] ^? folded . filtered (> 20) 
Nothing 

Bây giờ, giả sử "cách trực tiếp hơn" là một số thông minh Traversal: không có, đó là không thể, Traversal s không thể sửa đổi cấu trúc của điều đi qua.

+0

Tôi nghĩ rằng tôi phải đọc thêm một chút về thấu kính để hiểu điều này, nhưng cảm ơn bạn đã chỉ ra điều này! Tôi cho rằng không có gì giống như một 'Traversal' mà * có thể * thay đổi cấu trúc của điều đi qua? –

+0

Đó là chính xác, không có điều đó. Vấn đề là bạn không thể có bất kỳ luật nào tốt đẹp cho nó. –

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