Список против карты (безопасность ключей против сопоставления всех элементов) - PullRequest
6 голосов
/ 17 мая 2011

Какова лучшая структура данных для следующего сценария?

Скажем, у вас есть список URL

linkHaskell = Url "http://www.haskell.org"
linkReddit = Url "http://www.reddit.com"
...

и вы используете их по отдельности, но вы также хотите работать со всеми из них, например, проверка ссылок, вы можете поместить их в список

allLinks = [
    linkHaskell
  , linkReddit
  ...
  ]

Но это подвержено ошибкам, так как вы можете забыть добавить новую ссылку.

Вместо этого вы можете хранить эти URL-адреса на карте, но тогда вы будете обменивать ошибки времени компиляции на ошибки времени выполнения, если у вас есть опечатки в ключах.

В Хаскеле, что бы вы сделали?

1 Ответ

5 голосов
/ 18 мая 2011

Один простой подход заключается в определении типа данных для ссылок, например

data Link = LinkHaskell | LinkReddit
    deriving (Enum, Bounded)

toUrl LinkHaskell = Url "http://www.haskell.org"
toUrl LinkReddit  = Url "http://www.reddit.org"

allLinks :: [Link]
allLinks = [minBound .. maxBound]

Вам все равно придется указывать имя в двух местах, но по крайней мере теперь компилятор будет жаловаться, если вы забудете добавить егов одном месте (по крайней мере, с -Wall).

Другой подход - использовать магию Template Haskell:

{-# LANGUAGE TemplateHaskell #-}

module Links where

import Control.Monad
import Language.Haskell.TH

data Url = Url String
    deriving (Show)

mkLinks :: [(String, String)] -> Q [Dec]
mkLinks links = liftM2 (++) mkAllLinks $ mapM mkLink links
  where
    mkLink (name, url) = valD (varP $ mkLinkName name) (normalB [| Url url |]) []
    mkAllLinks = [d| allLinks = $(listE [varE $ mkLinkName name | (name, _) <- links] )|]
    mkLinkName = mkName . ("link" ++)

Теперь вам нужно указывать ссылки только в одном месте:

{-# LANGUAGE TemplateHaskell #-}

import Links

mkLinks
  [("Haskell", "http://www.haskell.org")
  ,("Reddit",  "http://www.reddit.org")
  ,("StackOverflow", "http://www.stackoverflow.com")
  ]

main = do
    putStrLn "By name:"
    print $ linkHaskell
    print $ linkReddit

    putStrLn "All:"
    mapM_ print allLinks
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...