以下情况的最佳数据结构是什么?
说,你有一个URL列表
linkHaskell = Url "http://www.haskell.org"
linkReddit = Url "http://www.reddit.com"
...
并且您单独使用它们,但您也希望对所有这些操作,例如链接检查,您可以将它们放在列表中
allLinks = [
linkHaskell
, linkReddit
...
]
但这很容易出错,因为您可能忘记添加新链接。
您可以选择将这些URL存储在Map中,但是如果您在密钥中存在拼写错误,则会为运行时错误交换编译时错误。
在Haskell你会做什么?
答案 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