迭代地打印List中的每个整数

时间:2014-03-05 16:42:21

标签: haskell ghc ghci

假设我有一个整数列表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的元素构造字符串的情况下执行此操作?

5 个答案:

答案 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。因此,我们将yys收集到列表中,然后将其返回到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

请注意,可以使用标准库中的mapMmapM_函数定义sequencesequence_而无需显式递归,这些函数正是其名称所暗示的。如果您查看mapMmapM_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