2013-06-13 13 views
8

Kayıtların bir listesi var ve belirli bir ada sahip bir kayıt için listeyi araştıran ve değeri değiştiren bir işleve ihtiyacım var Bu kayıttan VEYA, hiçbir kayıt eşleşmesi sonuç listesine yeni bir kayıt eklerse. Düzgün çalışırBazı koşullara karşı koruysa veya yeni bir tane eklerse, öğenin içeriğini değiştirirseniz, 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)] 

ancak (if -Construct olmadan) Data.Lens kullanarak bu yazı daha doğrudan bir yol olup olmadığını merak ediyorum: İşte benim kod şimdiye kadar mı? Ayrıca, _find işlevini yazmalı mıyım yoksa kitaplıkta eşdeğer bir şey var mı?

Güncelleme: İşte kaynağının Gist denemektir: https://gist.github.com/SKoschnicke/5795863

cevap

2

nereden: tekrarlama kurtulur

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

Yani, bir 'Prism' listeyi değiştirmeme izin veriyor? Bunu anlayabilmek için bunu test etmem gerektiğini düşünüyorum, bunu yarın yapacak! –

+0

Güzel çalışıyor! Ama anladığım kadarıyla, "filtrelenmiş" kullanımıyla, "pos" artık geçerli değil "Traversal", değil mi? Bunun için genellikle diğer traversler ile birleştirilemez. –

+0

Gerçekten de 'isim' alanını değiştirmek için kullanırsanız ve bu nedenle yüklemenin artık kullanılmamasını sağlarsanız ('kurallar', Matvey'in yanıtında gösterildiği gibi bozulabilir) geçerli bir Traversal işlevi değildir, ancak aksi halde diğerleriyle birleştirilebilir. dolaşımları. – yairchu

2

Bilmiyorum, ama sen Yani

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

, teşekkür ederim! –

2

gibi bazı yazabilir _find aslında Traversal değil :

> [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] 

Bu aynı anlama geliyor filtered travers değil ark.

bölüm filtered ile taklit edilebilir Başlarken (herhangi yasaları yok burada Fold beri iyidir):

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

Şimdi, "daha doğrudan yol" zekice Traversal olduğunu varsayarak: hayır, bu mümkün değil, Traversal s, geçiş yapılan şeyin yapısını değiştiremez.

+0

Bunu anlamak için mercekler hakkında daha fazla şey okumam gerektiğini düşünüyorum, ama bunu işaret ettiğin için teşekkürler! Traversed şeyin yapısını değiştirebilecek bir Traversal gibi bir şey olmadığını varsayıyorum. –

+0

Doğru, böyle bir şey yok. Sorun şu ki, bunun için güzel yasaların olamaz. –

İlgili konular