吉洪的解决方案是最简单的一种,但它有一个重大缺陷:它不会在处理整个列表之前产生任何结果,如果处理过大的列表,它会溢出。
更接近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>
不错,绝对接近Rx。谢谢! – Kr0e 2013-05-07 14:30:58
不客气! – 2013-05-07 14:36:18