Kr0*_*r0e 4 io monads haskell functional-programming
我有以下功能:
lines' :: [IO String]
lines' = getLine : lines'
我希望我可以在这个列表中使用所有强大的列表函数,比如过滤器等.但是我对haskell中IO monad的了解是可以改进的.
在使用Rx for C#之后,io_stuff-list列表使我信服.
有没有办法在哈斯克尔做我想做的事情?就像是:
ten_lines :: [IO String]
ten_lines = take 10 lines'
proc_lines :: [IO String]
proc_lines = [ (l, length l) | l <- lines' ]
谢谢!
Tik*_*vis 10
有一大堆正常的列表函数被修改为与monad一起使用Control.Monad.您的问题特别感兴趣:
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
(sequence并且mapM实际上是通过前奏导出的,默认情况下可用.)
例如,让我们看看你的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
Tikhon的解决方案是最简单的解决方案,但它有一个主要缺点:在处理整个列表之前它不会产生任何结果,如果处理过大的列表,它将会溢出.
更接近C#的Rx的解决方案是使用类似的流媒体库pipes.
例如,您可以定义从用户输入Producer生成Strings:
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>
| 归档时间: | 
 | 
| 查看次数: | 595 次 | 
| 最近记录: |