列表与地图(关键安全与所有元素的映射)

时间:2011-05-17 09:03:13

标签: data-structures haskell

以下情况的最佳数据结构是什么?

说,你有一个URL列表

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

并且您单独使用它们,但您也希望对所有这些操作,例如链接检查,您可以将它们放在列表中

allLinks = [
    linkHaskell
  , linkReddit
  ...
  ]

但这很容易出错,因为您可能忘记添加新链接。

您可以选择将这些URL存储在Map中,但是如果您在密钥中存在拼写错误,则会为运行时错误交换编译时错误。

在Haskell你会做什么?

1 个答案:

答案 0 :(得分:5)

一种简单的方法是为链接定义数据类型,即

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),编译器会抱怨。

另一种方法是使用一些模板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