【问题标题】:pipes for each evaluates before sending downstream?每个管道在向下游发送之前评估?
【发布时间】:2014-08-05 15:24:40
【问题描述】:

通过pipes tutorial 导致玩弄一些例子:

import Pipes
import qualified Pipes.Prelude as P

f1 :: Show a => Int -> [a] -> IO ()
f1 n xs = runEffect $ (for (each xs) (lift . putStrLn . show))
            >-> P.take n
            >-> P.stdoutLn

f2 :: Show a => Int -> [a] -> IO ()
f2 n xs = runEffect $ each xs
            >-> P.map show
            >-> P.take n
            >-> P.stdoutLn

但以上产生:

>>> f1 3 [1..10]
1
2
3
4
5
6
7
8
9
10
>>> f2 3 [1..10]
1
2
3
>>>

与我的预期相反,f1 和 f2 会产生相同的结果(即 f2 的结果)。问题是:他们为什么不呢?

【问题讨论】:

    标签: haskell haskell-pipes


    【解决方案1】:

    For 的类型签名为:

    for
      :: Monad m =>
         Proxy x' x b' b m a'
         -> (b -> Proxy x' x c' c m b') -> Proxy x' x c' c m a'
    

    for 的第二个参数是:

    (lift . putStrLn . show)
    

    它的类型为b -> Proxy ...,但由于yield 不存在,因此不会在下游产生任何东西。这意味着P.takeP.stdoutLn 永远不会运行。由于P.take 没有收到任何值,因此它不会关闭管道。

    如果您想使用 for 打印值并在下游输出,您可以:

    f1 :: Show a => Int -> [a] -> IO ()
    f1 n xs = runEffect $ (for (each xs) 
                               (\x -> (lift . putStrLn . show) x >> yield x))
                >-> P.show
                >-> P.take n
                >-> P.stdoutLn
    
    
    > f1 3 [1..10]
    1
    1
    2
    2
    3
    3
    

    编辑:

    以下是一些基于f1 使用for 的附加代码示例:

    f3 :: Show a => Int -> [a] -> IO ()
    f3 n xs = runEffect $ for (each xs >-> P.take n) (lift . putStrLn . show)  
    
    f4 :: Show a => Int -> [a] -> IO ()
    f4 n xs = runEffect $ each [1..10] >-> for (P.take n) (lift . putStrLn . show)
    
    > f3 3 [1..10]
    1
    2
    3
    > f4 3 [1..10]
    1
    2
    3
    

    【讨论】:

    • 更准确地说,原始示例中的takestdoutLn 都会短暂运行,直到它们到达它们的第一个await 语句,然后它们永远不会重新获得控制权。
    • 另外,您可以使用Pipes.Prelude.chain 轻松运行操作并将原始输入转发到下游。
    猜你喜欢
    • 1970-01-01
    • 2017-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-02
    • 2021-03-16
    • 1970-01-01
    相关资源
    最近更新 更多