如何更新Haskell地图中的项目?

时间:2018-07-04 19:23:34

标签: dictionary haskell

我是Haskell的新手,正在尝试找出明智的方法 写入地图的方式(为解决特定问题做准备 欧拉项目问题)

我希望编写一个可以填充的函数 带有记录的地图。但是我无法正常工作。

let似乎在创建局部变量,而不是
smap视为全局。

必须有某种方法可以做到这一点。

我的代码:

import Data.Map (Map)
import qualified Data.Map as Map 

smap = Map.fromList [("cocoa",23)]


newdata str n = do  
   let cpy  = Map.insert str n  smap
   cpy 

main = do
     let smap = newdata "pennywise" 16  
     let smap = newdata "krusty" 18  

从评论中更新:稍后,我想计算一个直角三角形等于周长的方式。所以我认为地图将是存储分配计数的好方法,例如p10-> 5种方式,p15-> 6种方式,等等。因此,在程序运行时,它将增加已经发现的周长值。

2 个答案:

答案 0 :(得分:5)

您不能就地修改Map(因为Haskell是一种纯粹的功能语言),但是您可以创建一个几乎等于旧地图的新语言,除了一些已修改的条目。

(不必太担心效率:与直觉相反,新的Map不需要完整的旧副本。)

例如,假设我们要计算字符串中每个字符的频率。让我们编写一个函数,给定一个字符c,该函数增加存储在Map内的计数

import qualified Data.Map.Strict as M

countChar :: Char -> M.Map Char Int -> M.Map Char Int
countChar c oldMap = newMap
   where
   newMap = M.insertWith (+) c 1 oldMap

不需要newMap变量,为清楚起见,上面显示了该变量。

函数insertWith生成新映射,因此如果旧映射中没有值,则在索引c处存储1,如果先前值{ {1}}在旧地图中。

要处理完整的字符串,我们使用递归:

1 + x

GHCi中的小测试:

x

对于更高级的解决方案,如果需要,countString :: String -> M.Map Char Int countString "" = M.empty countString (c:cs) = countChar c (countString cs) 也可以重写为折叠。使用左严格折叠也可以提高效率。

> countString "here's an example"
fromList [(' ',2),('\'',1),('a',2),('e',4),('h',1),('l',1),('m',1)
         ,('n',1),('p',1),('r',1),('s',1),('x',1)]

甚至可以使用状态monad来避免绕过countString。如果您正在学习Haskell,请不必担心,从开始学习如何使用递归,模式匹配和countString = foldl' (flip countChar) M.empty 的一些库函数来解决这类任务。

答案 1 :(得分:2)

Haskell是一种纯函数式语言。您不能就地修改变量。但是使用状态单子和镜头,Haskell可以成为最好的命令式语言。这是一个例子。

import Control.Lens
import Data.Map
import Control.Monad.State

example :: State (Map String Int) Int
example = do
    -- set value
    at "pennywise" ?= 16
    at "krusty" ?= 18
    -- get value
    Just krusty <- use $ at "krusty"
    pure krusty

main :: IO ()
main = do
    let r = evalState example empty
    print r

Lens提供了一个通用接口来处理诸如Map之类的数据结构,这就是我喜欢它的原因。

另一个例子:

countString :: String -> Map Char Int
countString str = flip execState empty $
    forM_ str $ \c -> 
        at c %= Just . maybe 1 (+1)

-- countString "asasdsas"
-- fromList [('a',3),('d',1),('s',4)]