2013-05-07 91 views
4

我有以下功能:哈斯克尔处理[IO字符串]

lines' :: [IO String] 
lines' = getLine : lines' 

我希望我可以只使用所有的勇士名单上的功能此 名单,像过滤器等,但我对知识IO monad在 haskell是可以改进的。

在使用Rx for C#后,io_stuff概念列表令我信服。

有什么办法可以做到我想要的哈斯克尔? 类似于:

ten_lines :: [IO String] 
ten_lines = take 10 lines' 

proc_lines :: [IO String] 
proc_lines = [ (l, length l) | l <- lines' ] 

谢谢!

回答

8

吉洪的解决方案是最简单的一种,但它有一个重大缺陷:它不会在处理整个列表之前产生任何结果,如果处理过大的列表,它会溢出。

更接近C#的Rx的解决方案是使用流式库,如pipes

例如,可以定义一个Producer,从用户输入来生成String S:

import Control.Monad 
import Control.Proxy 

lines' :: (Proxy p) =>() -> Producer p String IO r 
lines'() = runIdentityP $ forever $ do 
    str <- lift getLine 
    respond str 

然后,可以定义一个阶段的是需要10行:

take' :: (Monad m, Proxy p) => Int ->() -> Pipe p a a m() 
take' n() = runIdentityP $ replicateM_ n $ do 
    a <- request() 
    respond a 

...然后处理阶段:

proc :: (Monad m, Proxy p) =>() -> Pipe p String (String, Int) m r 
proc() = runIdentityP $ forever $ do 
    str <- request() 
    respond (str, length str) 

...并最终输出阶段:

print' :: (Proxy p, Show a) =>() -> Consumer p a IO r 
print'() = runIdentityP $ forever $ do 
    a <- request() 
    lift $ print a 

现在你可以撰写这些为处理链并运行它:

main = runProxy $ lines' >-> take' 10 >-> proc >-> print' 

。 ..它会在输入每行后立即输出处理结果,而不是在最后提供批处理结果:

$ ./pipes 
Apple<Enter> 
("Apple",5) 
Test<Enter> 
("Test",4) 
123<Enter> 
("123",3) 
4<Enter> 
("4",1) 
5<Enter> 
("5",1) 
6<Enter> 
("6",1) 
7<Enter> 
("7",1) 
8<Enter> 
("8",1) 
9<Enter> 
("9",1) 
10<Enter> 
("10",2) 
$ 

实际上,您不必自己定义这些管道。您可以在pipes标准库中的组件中组装相同的链:

>>> runProxy $ stdinS >-> takeB_ 10 >-> mapD (\x -> (x, length x)) >-> printD 
<exact same behavior> 
+0

不错,绝对接近Rx。谢谢! – Kr0e 2013-05-07 14:30:58

+0

不客气! – 2013-05-07 14:36:18

10

有一大堆正常列表函数被修改为与中的单子一起使用。特别关注你的问题:

sequence :: Monad m => [m a] -> m [a] 
mapM  :: Monad m => (a -> m b) -> [a] -> m [b] 
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a] 
foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a 

sequencemapM由前奏实际出口和默认情况下可用)

例如,让我们来看看你take 10 lines'的类型。例如:

Prelude Control.Monad> :t take 10 lines' 
take 10 lines' :: [IO String] 

我们希望将此[IO String]转换为单个IO [String]操作。这正是sequence所做的!我们可以通过类型签名来说明这一点。所以:

sequence $ take 10 lines' 

会做你想做的。

这些功能中的大多数还有一个以_结尾的版本,如sequence_。这与正常函数的效果完全相同,除了它抛弃结果之外,返回()。那就是sequence_ :: [m a] -> m()。这是一个很好的选择,只要你不关心结果有两个原因:它对你的意图更明确,表现会更好。

所以,如果你想打印,而不是让他们 10行,你会写是这样的:

printLines = putStrLn "foo" : printLines 

main = sequence_ $ take 10 printLines 
+0

啊,这就是我正在寻找的!谢谢!! – Kr0e 2013-05-07 09:46:01

+2

此外,请看['replicateM'](http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Monad.html#v:replicateM)和['replicateM_' ](http://www.haskell.org/ghc/docs/latest/html/libraries/base/Control-Monad.html#v:replicateM_)。 – hammar 2013-05-07 11:34:24