已知键存在时,如何规避Haskell中的Map.lookup等偏函数?
避免Haskell中
fromJust与Map.lookup偏函数使用的方案 核心思路
你已经明确所有需要替换的子组件都存在于映射中,只是想规避fromJust这类偏函数(它们在理论上可能触发未定义行为)。解决方案的核心是显式处理Maybe分支,或使用更安全的总函数替代偏函数调用。
方法1:显式case处理Maybe分支
直接用case表达式处理Map.lookup的结果,为不可能触发的Nothing分支提供明确的错误信息。这样代码是总函数,且错误信息比fromJust更具可读性:
replaceInts :: [(Char,Int)] -> [(Char,Foo)] replaceInts ins = let subcomps :: [Int] subcomps = nub $ snd <$> ins myMap :: Map.Map Int Foo myMap = Map.fromList $ algo subcomps replInt :: (Char,Int) -> (Char,Foo) replInt (c, x) = case Map.lookup x myMap of Just foo -> (c, foo) -- 理论上不会触发,此处给出调试友好的错误信息 Nothing -> error $ "Internal bug: Int " ++ show x ++ " missing from replacement map" in replInt <$> ins
方法2:使用Map.findWithDefault
Map.findWithDefault是总函数,你可以传入一个永远不会被触发的默认值(比如带明确错误信息的error调用),既避免了fromJust,又保证代码安全性:
replaceInts :: [(Char,Int)] -> [(Char,Foo)] replaceInts ins = let subcomps :: [Int] subcomps = nub $ snd <$> ins myMap :: Map.Map Int Foo myMap = Map.fromList $ algo subcomps replInt :: (Char,Int) -> (Char,Foo) replInt p = (Map.findWithDefault (error "Impossible: key missing") myMap) <$> p in replInt <$> ins
方法3:用Data.IntMap优化(针对Int键)
因为你的键是Int类型,可使用Data.IntMap.Strict替代Data.Map.Strict,它的findWithDefault同样是总函数,且针对Int键有性能优化:
import qualified Data.IntMap.Strict as IntMap replaceInts :: [(Char,Int)] -> [(Char,Foo)] replaceInts ins = let subcomps :: [Int] subcomps = nub $ snd <$> ins myMap :: IntMap.IntMap Foo myMap = IntMap.fromList $ algo subcomps replInt :: (Char,Int) -> (Char,Foo) replInt p = (IntMap.findWithDefault (error "Impossible: key missing") myMap) <$> p in replInt <$> ins
方法4:maybe函数简化分支处理
如果觉得case表达式繁琐,可用maybe函数处理Map.lookup结果,同样提供明确错误信息:
replInt :: (Char,Int) -> (Char,Foo) replInt (c, x) = (c, maybe (error $ "Key " ++ show x ++ " not found") id (Map.lookup x myMap))
验证效果
以上修改均保持原代码功能,运行replaceInts testlist会得到相同输出:
[('a',27),('c',7),('d',0),('d',27),('a',7)]
内容的提问来源于stack exchange,提问作者Jogger
相关产品推荐
相关产品推荐

