我正在尝试使用Data.Map编写一个小程序来存储员工ID和名称,您可以插入,删除,查找等。
import qualified Data.Map as M
main = do
let emptymap = M.empty
map1 = M.insert 10001 "John" emptymap
print (M.lookup 10001 map1)
从这里开始,如果我将另一名员工添加到map1,我是否需要为其命名?如果我添加了以下行,则不会编译。
map1 = M.insert 10002 "Paul" map1
但是,如果我添加以下行,它可以工作(虽然它提供了很多警告)
map2 = M.insert 10002 "Paul" map1
我可以使用fromList()创建一个包含两名员工的新地图,但每次添加新员工(或删除员工)时,我都必须为结果指定一个新名称?我想要一个具有相同名称的最新信息的地图。请帮忙。
答案 0 :(得分:2)
不一定;您可以通过合成将插入链接在一起:
main = do
let emptymap = M.empty
map = M.insert 10001 "John" . M.insert 10002 Paul $ emptymap
print (M.lookup 10001 map)
如果您想稍后添加的其他名称,则需要一个新名称;否则,你定义一个“无限”的地图。
main = do
let emptymap = M.empty
map = M.insert 10001 "John" . M.insert 10002 Paul $ emptymap
print (M.lookup 10001 map)
let map' = M.insert 10003 "Kate" map
print (M.lookup 10003 map)
但是,如果您需要访问多个地方的相同地图,您可能希望在State
monad中工作,该monad将负责管理您对地图的所有引用。
答案 1 :(得分:1)
Haskell中的变量是不可变的。如果您想要可变状态,可以使用可变引用类型,例如IORef
(或STRef
):
import Data.IORef (modifyIORef', newIORef, readIORef)
import qualified Data.Map as Map
type Employees = Map Id Name
type Id = Int
type Name = String
main :: IO ()
main = do
employees <- newIORef (Map.empty :: Employees)
addEmployee employees 10001 "John"
addEmployee employees 10002 "Paul"
print . Map.toList =<< readIORef employees
addEmployee :: IORef Employees -> Id -> Name -> IO ()
addEmployee employees id name
= modifyIORef' employees (Map.insert id name)
然而,这不是非常可测试或可组合 - 您仍然坚持使用IO
(或ST
)。您可以使用State
使其纯净:
import Control.Monad.Trans.State (evalState, modify, gets)
main :: IO ()
main = print $ flip evalState (Map.empty :: Employees) $ do
modify $ addEmployee 10001 "John"
modify $ addEmployee 10002 "Paul"
gets Map.toList
addEmployee :: Id -> Name -> Employees -> Employees
addEmployee = Map.insert
然后,关于状态的所有修改和查询函数(例如addEmployee
)都可以是纯粹的,您可以单独测试它们:
removeEmployee 10001 (addEmployee 10001 "John" Map.empty) == Map.empty
findEmployee 10002 Map.empty == Nothing
如果您需要将其他一些效果(例如I / O)与此状态交错,则可以使用StateT
:
import Control.Monad.Trans.Class (lift)
main :: IO ()
main = do
result <- flip evalStateT (Map.empty :: Employees) $ do
lift $ putStrLn "modifying"
modify $ addEmployee 10001 "John"
lift $ putStrLn "modifying again"
modify $ addEmployee 10002 "Paul"
lift $ putStrLn "returning"
gets Map.toList
print result