假设我有一个整数列表l = [1,2]
我要打印到stdout
。
执行print l
会产生[1,2]
说我想打印没有大括号的列表
map print l
生成
No instance for (Show (IO ())) arising from a use of `print'
Possible fix: add an instance declaration for (Show (IO ()))
In a stmt of an interactive GHCi command: print it
`:t print
print :: Show a => a -> IO ()
因此,虽然我认为这可行,但我继续尝试:
map putStr $ map show l
因为我怀疑从Integer到String的类型不匹配是罪魁祸首。这产生了与上面相同的错误信息。
我意识到我可以做一些事情,比如将列表连接成一个字符串,但如果可能的话,我想避免这样做。
发生了什么事?如何在不从List的元素构造字符串的情况下执行此操作?
答案 0 :(得分:6)
问题在于
map :: (a -> b) -> [a] -> [b]
所以我们最终得到[IO ()]
。这是一个纯值,一个IO
动作列表。它实际上不会打印任何东西。相反,我们想要
mapM_ :: (a -> IO ()) -> [a] -> IO ()
命名约定*M
表示它在monad上运行,*_
表示我们丢弃该值。这就像地图一样,除了它使用>>
对每个操作进行排序以返回IO
操作。
例如mapM_ print [1..10]
将在新行上打印每个元素。
答案 1 :(得分:2)
Haskell中的所有内容都是非常强类型的,包括执行IO的代码!
当您编写print [1, 2]
时,这只是putStrLn (show [1, 2])
的便捷包装器,其中show是一个将(Show'able)对象转换为字符串的函数。 print
本身不会执行任何事情(在执行的副作用意义上),但它会输出IO()
动作,这是一种就像一个迷你的非运行“程序”(如果你原谅那些草率的语言),它不是在创建时“运行”,而是可以传递以供以后执行。您可以在ghci中验证类型
> :t print [1, 2]
print [1, 2]::IO()
这只是IO ()
类型的对象....你现在可以抛弃它,什么都不会发生。更有可能的是,如果您在main
中使用此对象,则IO代码将运行,副作用等等。
当您将多个putStrLn
(或print
)函数映射到列表中时,您仍然可以获得一个可以在ghci中查看其类型的对象
> :t map print [1, 2]
map print [1, 2]::[IO()]
和以前一样,这只是一个你可以传递的对象,它本身不会做任何事情。但与之前不同的是,在main中使用的类型是不正确的,它需要一个IO()对象。要使用它,您需要将其转换为此类型。
有很多方法可以进行此转换....我喜欢的一种方法是sequence
函数。
sequence $ map print [1, 2]
其中列出了IO操作(即带有副作用的迷你“程序”,如果您将原谅这种草率的语言),并将它们排序为IO操作。这个代码现在可以做你想要的了。
正如jozefg指出的那样,尽管sequence
有效,sequence_
在这里是更好的选择....
序列不仅可以在IO操作中合并内容,还可以将返回值放在列表中....由于print的返回值是IO(),新的返回值将成为()的无用列表(在IO中) 。 :)
答案 2 :(得分:2)
假设您获得了一个列表xs :: [a]
和一个函数f :: Monad m => a -> m b
。您希望将函数f
应用于xs
的每个元素,生成一系列操作,然后对这些操作进行排序。以下是我将如何构建一个函数,称之为mapM
,这样做。在基本情况下,xs = []
是空列表,我们只返回[]
。在递归的情况下,xs
的格式为x : xs
。首先,我们要将f
应用于x
,并执行操作f x :: m b
。接下来,我们希望在mapM
上递归调用xs
。执行第一步的结果是一个值,比如y
;执行第二步的结果是值列表,比如ys
。因此,我们将y
和ys
收集到列表中,然后将其返回到monad中:
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
mapM f [] = return []
mapM f (x : xs) = f x >>= \y -> mapM f ys >>= \ys -> return (y : ys)
现在我们可以映射像print
这样的函数,它在IO
monad中返回一个要打印的值列表:mapM print [1..10]
对整数列表执行此操作从一到十。但是有一个问题:我们并不特别关注收集印刷业务的结果;我们主要关注它们的副作用。我们只返回y : ys
。
()
mapM_ :: Monad m => (a -> m b) ->[a] -> m ()
mapM_ f [] = return ()
mapM_ f (x : xs) = f x >> mapM_ f xs
请注意,可以使用标准库中的mapM
和mapM_
函数定义sequence
和sequence_
而无需显式递归,这些函数正是其名称所暗示的。如果您查看mapM
中mapM_
和Control.Monad
的源代码,您会看到它们以这种方式实现。
答案 3 :(得分:1)
使用lens库:
[1,2,3] ^! each . act print
答案 4 :(得分:0)
您也可以编写自己的函数:
Prelude> let l = [1,2]
Prelude> let f [] = return (); f (x:xs) = do print x; f xs
Prelude> f l
1
2