2015-05-17 7 views
6

Ich möchte mit Kmetts lens library auf ein Element einer (Schlüssel-, Wert-) Liste unter einem bestimmten Schlüssel zugreifen. wenn Sie verwenden Data.Map.Map warenVerwenden Sie ein Objektiv, um ein bestimmtes Element einer (Schlüssel-, Wert-) Liste zu ersetzen

-- | Looks for a given header and returns the value, if any 
fetchHeader :: Headers -> ByteString -> Maybe ByteString 
fetchHeader headers header_name = 
    snd <$> find (\ x -> fst x == header_name) headers 

-- | replaceHeaderValue headers header_name maybe_header_value looks for 
-- header_name. If header_name is found and maybe_header_value is nothing, it 
-- returns a new headers list with the header deleted. If header_name is found 
-- and header_value is Just new_value, it returns a new list with the header 
-- containing the new value. If header_name is not in headers and maybe_header_value 
-- is Nothing, it returns the original headers list. If header_name is not in headers 
-- and maybe_header_value is Just new_value, it returns a new list where the last element 
-- is (header_name, new_value) 
replaceHeaderValue :: Headers -> ByteString -> Maybe ByteString -> Headers 
replaceHeaderValue headers header_name maybe_header_value = 
    disect id headers 
    where 
    disect builder [] = case maybe_header_value of 
     Nothing -> headers 
     Just new_value -> builder $ (header_name, new_value):[] 
    disect builder ([email protected](hn,hv) : rest) 
     | hn /= header_name = 
      disect 
       (\ constructed_list -> builder $ el:constructed_list) 
       rest 
     | otherwise = case maybe_header_value of 
      Nothing -> builder rest 
      Just new_value -> builder $ (hn, new_value):rest  

Antwort

4

Nun: Mit anderen Worten, würde Ich mag diesen Code mit etwas mehr idiomatischen und vielleicht kürzer ersetzen:

wie unten
type Headers = [ (ByteString, ByteString) ] 

headerLens :: 
    Functor f => 
    ByteString -> 
    (Maybe ByteString -> f (Maybe ByteString)) -> 
    Headers -> 
    f Headers 
headerLens header_name f headers 
    | old_header_value <- fetchHeader headers header_name = fmap 
     (\ new_header_value -> 
       replaceHeaderValue 
       headers 
       header_name 
       new_header_value 
     ) 
     (f old_header_value) 

wo die Support-Funktionen definiert werden statt als Struktur (sollte ziemlich einfach sein refactor) Sie werden nicht replizieren müssen all diese Texte selbst arbeiten:

import qualified Data.Map as M 
import Control.Lens 

type Headers = M.Map ByteString ByteString 

fetchHeader :: Headers -> ByteString -> Maybe ByteString 
fetchHeader = flip M.lookup 

replaceHeaderValue :: Headers -> ByteString -> Maybe ByteString -> Headers 
replaceHeaderValue headers header_name maybe_header_value 
    = M.alter (const maybe_header_value) header_name headers 

Dann könnten Sie Ihrehaltenwie es ist. Oder Sie könnten wie folgt aussehen in etwas

headerLens name = lens (M.lookup name) (\hs mhv -> M.alter (const mhv) name hs) 

, die nicht die unterstützende Funktionen überhaupt nicht brauchen, und eine ziemlich generische Signatur für die Arbeit mit mehreren Arten von Map s haben. Beispielverwendung:

Die Reihenfolge der Elemente bleibt jedoch erhalten, was für Sie ein Problem darstellen könnte. Wahrscheinlich gibt es dort irgendwo eine geordnete Karte, ähnlich wie Pythons OrderedDict, aber ich habe sie noch nie in Haskell benutzt.

+0

Dank @bheklilr. Sie haben Recht damit, dass die Bestellung wichtig ist, und generell habe ich von der Optimierung vor dem Profilieren abgesehen. – dsign

+0

@dsign Wenn das der Fall ist, dann benutze einfach deinen 'Header' Typ, ersetze' M.lookup' durch 'Prelude.lookup' und implementiere [' Data.Map.alter'] (http: //hackage.haskell. org/package/containers-0.5.6.3/docs/src/Data-Map-Base.html # ändern) für Assoziationslisten, was das knifflige Bit sein wird. Oder du könntest eine Weile nach Hackage suchen, um herauszufinden, ob jemand anderes das schon getan hat, weil sie es wahrscheinlich getan haben. – bheklilr

+0

Guter Punkt. Ein fieses kleines Detail, das ich gerade realisiert habe, ist, dass ich die Objektivgesetze in meinem aktuellen Setup nicht befolgen kann .... also denke ich, dass ich zu einer Karte wechseln und die Tasten mit einer benutzerdefinierten Funktion bestellen muss (es ist nicht lexikographisch, aber es gibt eine * richtige * Bestellung). Vielen Dank! – dsign

Verwandte Themen