最近我有一个字符串列表,需要独立遍历每个字符串并执行一些IO
功能。
基本上我所拥有的是:
goOverList :: [String] -> IO ()
goOverList (x:[]) = do
putStrLn x
goOverList (x:xs) = do
goOverList [x]
goOverList xs
main = do
let myList = ["first", "second", "third"]
goOverList myList
我的IO
有点复杂,但这是它的要点(需要让一个函数遍历列表并根据列表成员执行IO
)我希望有人可能会显示我怎么做得更好。
答案 0 :(得分:15)
您的goOverList
功能几乎等同于mapM_ putStrLn
。 (差不多因为mapM_
也适用于空列表而你的函数没有)。
mapM
是一个函数,它将a -> IO b
¹类型的函数应用于a
列表中的每个项目,并返回一个IO
²列表b
的。{ mapM_
与mapM
相同,只是它不会将结果存储在列表中(对于()
返回putStrLn
的操作没有意义。
¹实际上它比这更通用:函数的类型为a -> m b
Monad m
,但在这种情况下,m
为IO
。
²实际上它实际上是m。
答案 1 :(得分:9)
sepp2k和solrize推荐mapM_
是正确的。但是,本着教导你钓鱼而不是给你一条鱼的精神,这里有你可以尝试的东西:
(String -> IO ()) -> [String] -> IO ()
类型的内容。String
替换为a
以获取(a -> IO ()) -> [a] -> IO ()
,并搜索该内容。现在the second search的第三个结果是mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
,这正是您想要的。 (第一个答案,closeFdWith :: (Fd -> IO ()) -> Fd -> IO ()
不是相关结果;第二个,traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
是相关的,但理解和使用起来有点复杂。)
答案 2 :(得分:4)
首先,轻松改进:
goOverList' :: [String] -> IO ()
goOverList' [] = return ()
goOverList' (x:xs) = do
putStrLn x
goOverList' xs
递归的基本情况应该是空列表:在这种情况下,您只需返回不执行任何操作的IO操作return ()
。如果您有一个或多个元素,则打印它并继续使用列表的其余部分,这很简单。
同样精确的事情可以通过mapM_ :: Monad m => (a -> m b) -> [a] -> m ()
更紧凑的定义来实现:它与常规map
相同,除了它适用于monadic动作。它不会像常规m [b]
那样返回结果集合mapM
,而是将其抛弃。在这种情况下,它对你来说效果很好,因为你只对打印出列表中的元素感兴趣。
goOverList'' :: [String] -> IO ()
goOverList'' = mapM_ putStrLn
为了更加通用,我们可以依靠print :: Show a => a -> IO ()
代替putStrLn
并在输入中接受每个“可展示”项目列表:
goOverList''' :: (Show a) => [a] -> IO ()
goOverList''' = mapM_ print
data T = One | Two | Three deriving (Show)
main = do
let myList = [One, Two, Three]
goOverList''' myList
答案 3 :(得分:2)
您的goOverList
函数可以写为mapM_ putStrLn
,其中mapM_
是标准前奏中的函数。
您还可以简化自己的实施:
goOverList :: [String] -> IO ()
goOverList [] = return ()
goOverList (x:xs) = do
putStrLn x
goOverList xs