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

Dictionary 如何更新Haskell地图中的项目?,dictionary,haskell,Dictionary,Haskell,我是哈斯克尔的新手,正在努力找出一个合理的解决办法 写入地图的方式(为解决特定问题做准备) 欧拉项目问题) 我希望编写一个函数来填充 有记录的地图。但我不能让它工作 let似乎创建局部变量,而不是 将smap视为一个整体 一定有办法做到这一点 我的代码: import Data.Map (Map) import qualified Data.Map as Map smap = Map.fromList [("cocoa",23)] newdata str n = do let

我是哈斯克尔的新手,正在努力找出一个合理的解决办法 写入地图的方式(为解决特定问题做准备) 欧拉项目问题)

我希望编写一个函数来填充 有记录的地图。但我不能让它工作

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路等。因此,当程序运行时,它将增加已发现的周长值。

您不能在适当的位置修改
映射(因为Haskell是一种纯函数式语言)但您可以创建一个几乎与旧地图相同的新地图,除了一些已修改的条目

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

例如,假设我们要计算字符串中每个字符的频率。让我们编写一个函数,给定一个char
c
,它会增加
映射中存储的计数

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+x
如果旧映射中有以前的值
x

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

countString :: String -> M.Map Char Int
countString ""     = M.empty
countString (c:cs) = countChar c (countString cs)
GHCi中的小型测试:

> 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)]
对于更高级的解决方案,
countString
也可以根据需要重写为折叠。使用左严格折叠也可以提高效率

countString = foldl' (flip countChar) M.empty

人们甚至可以使用状态单子来避免传递
映射
。如果您正在学习Haskell,不要担心这一点,从学习如何使用递归、模式匹配和一些用于
Map
s的库函数来解决这类任务开始。

Haskell是一种纯函数式语言。不能就地修改变量。但是有了状态monad和lens,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

变量是不可变的,您不能真正“更新”它们。一旦分配,它们不会改变。你现在拥有的可以替换为:
smap=Map.fromList[(“cocoa”,23),(“pennywise”,16),(“krusty”,18)]
你能说明为什么这还不够吗?嗨,Norrius,稍后我想计算一下直角三角形与周长相等的方式有多少。因此,我认为映射是存储分布计数的一种好方法,例如p10->5路、p15->6路等。因此,当程序运行时,它将增加已发现的周长值。您始终可以使用
Map.insert
创建一个新映射并使用它。我认为我是通过将Map.insert分配给cpy来实现这一点的。是否有我缺少的东西。不要尝试重新分配它,只使用它,例如在递归中使用它进行下一次函数调用。您可以尝试编写实际的代码,首先进行计数,以查看您需要它的位置。我不太关心
insertWith
:很难记住参数传递到组合函数的顺序。如果您需要的不仅仅是
insert
,我认为
alter
提供了更好的API。
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)]