我想从stdin读取字符串并将它们存储到地图中,其中key是输入字符串,并且value是此字符串之前出现的次数.在Java中我会做这样的事情:
for (int i = 0; i < numberOfLines; i++) {
input = scanner.nextLine();
if (!map.containsKey(input)) {
map.put(input, 0);
System.out.println(input);
} else {
int num = map.get(input) + 1;
map.remove(input);
map.put(input, num);
System.out.println(input.concat(String.valueOf(num));
}
}
Run Code Online (Sandbox Code Playgroud)
我已经尝试过在Haskell中使用forM_但是没有运气.
import Control.Monad
import qualified Data.Map as Map
import Data.Maybe
main = do
input <- getLine
let n = read input :: Int
let dataset = Map.empty
forM_ [1..n] (\i -> do
input <- getLine
let a = Map.lookup input dataset
let dataset' =
if isNothing a then
Map.insert input 0 dataset
else
Map.insert input num (Map.delete input dataset)
where num = ((read (fromJust a) :: Int) + 1)
let dataset = dataset'
let output = if isNothing a then
input
else
input ++ fromJust a
putStrLn output)
Run Code Online (Sandbox Code Playgroud)
dataset上面代码中的内容根本没有变化.
你的问题是Map.insert不能map.remove用C++ 做什么. Map.insert返回一个新的Map,其中包含元素,但你只是抛弃了这个新的Map.这就是几乎所有Haskell数据结构的工作方式,例如代码:
main = do
let x = []
y = 5 : x
print x
Run Code Online (Sandbox Code Playgroud)
打印空列表[].cons :运算符不会破坏性地修改空列表,但返回包含的新列表5. Map.insert使用地图而不是列表执行相同操作.
的Map定义中Data.Map是不可变的数据类型.调用Map.insert返回修改后的内容Map,它不会更改您已有的内容.您想要做的是在循环中迭代地应用更新.更喜欢的东西
import qualified Data.Map as M
import Data.Map (Map)
-- Adds one to an existing value, or sets it to 0 if it isn't present
updateMap :: Map String Int -> String -> Map String Int
updateMap dataset str = M.insertWith updater str 0 dataset
where
updater _ 0 = 1
updater _ old = old + 1
-- Loops n times, returning the final data set when n == 0
loop :: Int -> Map String Int -> IO (Map String Int)
loop 0 dataset = return dataset
loop n dataset = do
str <- getLine
let newSet = updateMap dataset str
loop (n - 1) newSet -- recursively pass in the new map
main :: IO ()
main = do
n <- fmap read getLine :: IO Int -- Combine operations into one
dataset <- loop n M.empty -- Start with an empty map
print dataset
Run Code Online (Sandbox Code Playgroud)
注意这实际上是如何减少代码(如果你只计算出现次数,它会更短updateMap dataset str = M.insertWith (+) str 1 dataset),它将纯代码与不纯的代码分开.
在这种情况下,您实际上并不想使用forM_,因为计算的每个步骤都取决于之前的步骤.编写一个在某种条件下退出的递归函数是首选.如果你需要的话,你也可以写loop为
loop :: Int -> IO (Map String Int)
loop n = go n M.empty
where
go 0 dataset = return dataset
go n dataset = getLine >>= go (n - 1) . updateMap dataset
Run Code Online (Sandbox Code Playgroud)
在这里,我将旧的主体压缩loop成一行,然后将其放入内部go,这允许您将其称为
main :: IO ()
main = do
n <- fmap read getLine :: IO Int
dataset <- loop n
print dataset
Run Code Online (Sandbox Code Playgroud)
这消除了需要知道,你必须传递M.empty到loop了第一个电话,除非你有一个用例调用loop同一个地图上多次.
| 归档时间: |
|
| 查看次数: |
909 次 |
| 最近记录: |