2015-05-17 14 views
6

Tôi muốn sử dụng số lens library của Kmett để truy cập vào phần tử của danh sách (khóa, giá trị) theo một khóa cụ thể. Nói cách khác, tôi muốn thay thế mã này với một cái gì đó nhiều thành ngữ và có lẽ ngắn hơn:Sử dụng ống kính để thay thế một phần tử cụ thể của danh sách (khóa, giá trị)

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) 

nơi các chức năng hỗ trợ được quy định như sau:

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

Trả lời

4

Vâng, nếu bạn đã sử dụng Data.Map.Map như cấu trúc của bạn thay vì (phải là một cấu trúc lại khá dễ dàng), bạn sẽ không cần phải sao chép tất cả điều này làm việc cho mình:

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 

Sau đó, bạn có thể giữcủa bạnnhư vậy. Hoặc bạn có thể nhìn vào một cái gì đó giống như

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

Mà không cần các chức năng hỗ trợ ở tất cả và có thể có một chữ ký khá chung chung để làm việc với nhiều loại Map s. Ví dụ về cách sử dụng:

> let hs = M.fromList [("foo", "bar")] 
> hs ^. headerLens "foo" 
Just "bar" 
> hs ^. headerLens "baz" 
Nothing 
> headerLens "foo" .~ Just "baz" $ hs 
fromList [("foo", "baz")] 
> headerLens "foo" .~ Nothing $ hs 
fromList [] 
> headerLens "qux" .~ Just "baz" $ hs 
fromList [("foo", "bar"), ("qux", "baz")] 
> headerLens "qux" .~ Nothing $ hs 
fromList [("foo", "bar")] 

Tuy nhiên, điều này sẽ không giữ nguyên thứ tự của các thành phần, có thể gây ra sự cố cho bạn. Có lẽ có một bản đồ được đặt hàng ở đâu đó, tương tự như của OrderedDict của Python, nhưng tôi đã không sử dụng nó trong Haskell trước đây.

+0

Cảm ơn @bheklilr. Bạn đang đúng về việc đặt hàng là quan trọng, và nói chung tôi đã kiềm chế từ tối ưu hóa trước khi hồ sơ. – dsign

+0

@dsign Nếu trường hợp đó xảy ra, bạn chỉ cần sử dụng loại 'Tiêu đề', thay thế' M.lookup' bằng 'Prelude.lookup' và triển khai lại [' Data.Map.alter'] (http: //hackage.haskell. org/package/containers-0.5.6.3/docs/src/Data-Map-Base.html # alter) cho các danh sách liên kết, đây sẽ là một mẹo nhỏ. Hoặc bạn có thể duyệt xung quanh Hackage một lúc để tìm xem liệu có ai khác đã làm điều đó chưa, bởi vì họ có thể có. – bheklilr

+0

Điểm tốt. Một chi tiết nhỏ khó chịu mà tôi vừa mới nhận ra là tôi không thể tuân thủ luật ống kính trong thiết lập hiện tại .... vì vậy tôi nghĩ tôi cần chuyển sang bản đồ và đặt các khóa bằng chức năng tùy chỉnh (không phải là từ điển, nhưng có một thứ tự * thích hợp *). Cảm ơn! – dsign

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