我是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种方式,等等。因此,在程序运行时,它将增加已经发现的周长值。
答案 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)]